文章链接:代码随想录
熟悉链表移除节点操作,分头节点和非头节点情况,头节点直接往后移一位,非头节点需要通过头节点指针重新指向它后一位的后一位进行遍历来操作
/**
* 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* removeElements(ListNode* head, int val) {
//当链头(头节点)不为空且链头的值为val时循环
while(head != nullptr && head->val == val)
{
//先设置一个临时空间存放现在的链头
ListNode* tmp = head;
//将现在链头的指针指向下一个位置,并存为新的链头
head = head->next;
//删掉临时空间(存的旧链头)
delete tmp;
}
//删除非链头(非头节点)中值为val的节点
//链表中,我们需要用链头(头节点)去确认其他节点的位置,也就是知道了头节点我们就知道了整个链表的具体位置
//所以这里我们需要先确认头节点
ListNode* cur = head;
//当这个节点(这里是链头)不为空,且它指向的下一个节点next不为空时循环,就可以依次找到所有节点
while(cur != nullptr && cur->next !=nullptr)
{
//判断当前节点(注意是头节点的下一个节点)的值是否为val
if(cur->next->val == val)
{
//先设置一个临时空间存需要删除的节点
ListNode* tmp = cur->next;
//将头节点所指向的下一个节点更新为它的下一个的下一个节点
cur->next=cur->next->next;
//删除临时空间(不需要的节点)
delete tmp;
}
else
{
//如果当前节点的值不为val;更新为新的cur,推动向前遍历
cur=cur->next;
}
}
//返回新的头节点
return head;
}
};
设置一个虚拟头节点来统一操作也可
/**
* 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* removeElements(ListNode* head, int val) {
//设置一个虚拟头节点
ListNode* dummyhead = new ListNode(0);
//真头节点存在虚拟头节点的后面
dummyhead->next = head;
//设置一个进行遍历的指针,从虚拟头节点开始
ListNode* cur = dummyhead;
//判断关键条件,真头节点不能为空
while(cur->next != nullptr)
{
if (cur->next->val == val){
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}
else{
//推进遍历
cur = cur->next;
}
}
head = dummyhead->next;
delete dummyhead;
return head;
}
};
文章讲解:代码随想录
比较多细节操作,多复习,注意遍历index, 是遍历(index-1)次,index=0 就停 相当于 false
class MyLinkedList {
public:
struct ListNode{
int val;
ListNode* next;
ListNode(int val):val(val), next(nullptr){}
};
MyLinkedList() {
dummyhead =new ListNode(0);
size=0;
}
int get(int index) {
// n>size-1 || n<0 不合法
if(index > (size-1) || index <0){
return -1;
}
// 设置cur节点用来遍历
ListNode* cur = dummyhead;
//向前遍历(index-1)次,idex=0 就停止,
while(index){
cur = cur->next;
index--;
}
return cur->next->val;
}
void addAtHead(int val) {
ListNode* newNode = new ListNode(val);
//必须要先调整新节点的尾巴指向再调整指向它的节点指向
newNode->next = dummyhead->next;
dummyhead->next = newNode;
size++;
}
void addAtTail(int val) {
ListNode* newNode = new ListNode(val);
ListNode* cur = dummyhead;
while(cur->next != nullptr){
cur = cur->next;
}
cur->next = newNode;
size++;
}
void addAtIndex(int index, int val) {
if(index > size) return;
if(index <0 ) index=0;
ListNode* newNode = new ListNode(val);
ListNode* cur = dummyhead;
while(index){
cur = cur->next;
index--;
}
newNode->next = cur->next;
cur->next = newNode;
size++;
}
void deleteAtIndex(int index) {
if (index >= size || index < 0) {
return;
}
ListNode* cur = dummyhead;
while(index--){
cur = cur->next;
}
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp = nullptr;
size--;
}
// void printLinkedList() {
// ListNode* cur = dummyhead;
// while (cur->next != nullptr) {
// cout << cur->next->val << " ";
// cur = cur->next;
// }
// cout << endl;
// }
private:
int size;
ListNode* 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);
*/
文章讲解:代码随想录
双指针传递法 (重点掌握)
形象的比喻有点像“占双人座”,你是cur, 朋友pre叫你帮他占个座,因为你也要坐,所以你叫临时朋友tmp帮你先在现场占1个座,你cur随后就到占了另1个座,等到pre来了,你把座位给了pre, 临时朋友temp再把座位给了你。pre是关键人物,所以pre先动,反转(换座)开始。
tmp存着下一个cur,cur的下一个是pre
/**
* 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) {
//设置一个pre节点等于空
//因为反过来了,所以原head节点成了尾节点,因此 tail->next = nullptr 对应 head->next = nullptr
ListNode* pre = nullptr;
//设置遍历指针,初始化指向头节点
ListNode* cur = head;
//遍历到cur到原链表尾后的空为止
while(cur!=nullptr){
//设置临时节点储存当前正常链表的下一个节点
ListNode* tmp = cur->next;
//开始反转,在第一轮中当前节点head的下一个节点是空节点pre;之后就是当前节点的下一个节点是pre
cur->next = pre;
//下面的思路主要在于不要赋给tmp,因为它储存了下一个节点
//在于平移pre和cur,注意先pre后cur,如果反了会导致tmp被传给了pre,反转失败
pre=cur;
cur=tmp;
}
return pre; //返回pre,是因为到最后,pre成了反转了的链表的头节点“head”
}
};
递归法,根据双指针解法写的递归函数
/**
* 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* reverse(ListNode* pre, ListNode* cur) {
//终止条件
if (cur == nullptr) return pre;
//交换逻辑
ListNode* tmp = cur->next;
cur->next = pre;
//cur传递给了pre, tmp传递给了cur 符合双指针解法中的 pre=cur, cur = tmp
return reverse(cur, tmp);
}
ListNode* reverseList(ListNode* head){
return reverse(nullptr, head);
}
};