链表操作的两种方式:
c++代码——讲解
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* freshhead = new ListNode(0);
freshhead->next = head;
ListNode* cut = freshhead;
while (cut->next != NULL) {
if (cut->next->val == val) {
ListNode* snode = cut->next;
cut->next = cut->next->next;
delete snode;
}
else {
cut = cut->next;
}
}
head = freshhead->next;
delete freshhead;
return head;
}
};
默认的结点定义(c++代码)
单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
//就可以
ListNode* head = new ListNode(5);
//还有一种方法:
ListNode* head = new ListNode();
head->val = 5;
重点
1.删除结点
一般删除结点后需要主动释放(delete)掉,java等语言会自动释放
2.虚拟头节点
为了便捷,删除结点都遵循一个规则,定义一个新的头节点freshhead
除了可以减少单独写删除头结点的代码,还能减少判断条件
while (cur != NULL && cur->next!= NULL)
//没虚拟头节点要判断一下本身是否为空,因为输入的有可能是空数组,
//如果没判断自己,就会少判断第一个元素
while (cut->next != NULL)
//右虚拟头节点不需要判断自己也能判断所有元素
//条件必须遍历到所有元素
3.代码理解
cut = cut->next;
cut->next = cut->next->next;
下一块空间的地址变成了下下一块空间的地址
c++代码------讲解
class MyLinkedList {
public:
struct LinkedNode {
int val;
LinkedNode* next;
};
//头结点
MyLinkedList() {
_dummyHead = new LinkedNode();
_size = 0;
}
// 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点
int get(int index) {
if (index > (_size - 1) || index < 0) {
return -1;
}
LinkedNode* cur = _dummyHead->next;//注意index是从0开始的,第0个节点就是头结点
while(index--){ // 如果--index 就会陷入死循环
cur = cur->next;//遍历不能破坏链表
}
return cur->val;
}
void addAtHead(int val) {
LinkedNode* Newnode = new LinkedNode();
Newnode->val = val;
Newnode->next = _d ummyHead->next;//因为没破坏数组可以不开一个新节点
_dummyHead->next = Newnode;
_size++;
}
void addAtTail(int val) {
LinkedNode* Newnode = new LinkedNode();
Newnode->val = val;
LinkedNode* cur = new LinkedNode();//不开结点会破坏列表
cur = _dummyHead;
while (cur -> next != NULL) {
cur = cur->next;
}
cur->next = Newnode;//cur下一个结点变成新元素
_size++;
}
// 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果index大于链表的长度,则返回空
// 如果index小于0,则置为0,作为链表的新头节点。
void addAtIndex(int index, int val) {
if (index > _size) {
return;
}
if (index < 0) {
index = 0;
}
LinkedNode* Newnode = new LinkedNode();
Newnode->val = val;
LinkedNode* cur = _dummyHead;
while(index--){
cur = cur->next;
}
Newnode->next = cur -> next;
cur->next = Newnode;
_size++;
}
void deleteAtIndex(int index) {
if (index >= _size || index < 0) {
return;
}
LinkedNode* cur = _dummyHead;
while (index--) {
cur = cur->next;
}
LinkedNode* snode = cur->next;
cur->next = cur->next->next;//改变的是真正的链表,就开了这一个结点
delete snode;
_size--;
}
private:
int _size;
LinkedNode* _dummyHead;
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
重点:
1.添加结点
代码:
Newnode->next = cur -> next;
cur->next = Newnode;
//比如先把f的下一个变成d,再把c的下一个变成f
注:=是变成的意思。
注意:
c++代码-----解法
双指针解法
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur = head;//快指针,当前需要变方向的元素
ListNode* pre = NULL;//慢指针,新链表当前的头下表
while (cur) {//当cur为null时结束
ListNode* temp = cur->next;
cur->next = pre;//改方向
pre = cur;//重复操作
cur = temp;
}
return pre;//头
}
};
图像
理解:就是指针不断后移不断改变方向来翻转链表,当cur到null后,pre就变成了头
代码
1. ListNode* temp = cur->next;
2.cur->next = pre;//改方向
3.pre = cur;//重复操作
4.cur = temp;
因为有重复代码我们可以用递归
class Solution {
public:
ListNode* reverse(ListNode* pre,ListNode* cur){
if(cur == NULL) return pre;
ListNode* temp = cur->next;
cur->next = pre;
// 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
// pre = cur;
// cur = temp;
return reverse(cur,temp);//重复代码
}
ListNode* reverseList(ListNode* head) {
// 和双指针法初始化是一样的逻辑
// ListNode* cur = head;
// ListNode* pre = NULL;
return reverse(NULL, head);
}
};