链表刷题总结

99 阅读7分钟

2. 链表

2.1 移除链表元素

思路1: 原地删除法 先是对头节点的处理,如果头节点一个或几个连着都是需要删除的val,向后移动的过程中保存tmp进行delete;接着是对待后面的节点,定义一个cur指针,cur->next是要删除的元素,删除之需要cur->next = cur->next->next即可

/**
* 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) {
       while(head != nullptr && head->val == val)
      {
           ListNode* tmp = head;
           head = head->next;
           delete tmp;
      }
​
       ListNode* cur = head;
       while(cur && cur->next)
      {
           if(cur->next->val == val)
          {
               ListNode* tmp = cur->next;
               cur->next = cur->next->next;
               delete tmp;
          }
           else cur = cur->next;
      }
​
       return head;
  }
};

思路2:虚拟头节点 建立一个虚拟头节点,指向原本的头节点,定义一个cur同理删除对应节点,最后将dummyHead->head赋值给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 && cur->next){
           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;
  }
};

2.2 设计链表

定义_size和虚拟头节点_dummyHead

初始化:虚拟头节点和_size的初始化

get获取:边界判断,走到对应位置获取,记得是第index个,从dummyHead->next开始走index步即可(举个例子想想,第0个节点是不是只要走0次)

addAtHead头插入:生成新节点,先连右边后连左边,最后别忘了_size++

addAtTail最后插入:生成新节点,走到最后一个节点,进行插入,别忘了_size++

addAtIndex:越界判断(过小过大都不行),生成新节点,从虚拟头,走index步,正好在要插入的第index个元素的前一个元素,进行插入,最后_size++

deleteAtIndex: 越界判断,走到第index的前一个,删除元素(删完临时节点记得置空),别忘了--_size

printLinkedList: 按序打印即可

class MyLinkedList {
public:
   // struct LinkedNode{
   //     int val;
   //     LinkedNode* next;
   //     LinkedNode(int val):val(val),next(nullptr){}
   // };
   struct LinkedNode {
       // int val;
       // LinkedNode* next;
       // LinkedNode(int val):val(val), next(nullptr){}
       int val;
       LinkedNode* next;
       LinkedNode(int val):val(val), next(nullptr){}
  };
​
   MyLinkedList() {
       //初始化
       _dummyHead = new LinkedNode(0);
       _size = 0;
  }
   
   int get(int index) {
       // if(index<0 || index > _size - 1)return -1;
       // LinkedNode* cur = _dummyHead->next;
       // while(index--)
       // {
       //     cur = cur->next;
       // }
       // return cur->val;
      if (index > (_size - 1) || index < 0) {
           return -1;
      }
       LinkedNode* cur = _dummyHead->next;
       while(index--){ // 如果--index 就会陷入死循环
           cur = cur->next;
      }
       return cur->val;
  }
   
   void addAtHead(int val) {
       // LinkedNode* node = new LinkedNode(val);
       // node->next = _dummyHead->next;
       // _dummyHead->next = node;
       // _size++;
       LinkedNode* newNode = new LinkedNode(val);
       newNode->next = _dummyHead->next;
       _dummyHead->next = newNode;
       _size++;
  }
   
   void addAtTail(int val) {
       // LinkedNode* cur = _dummyHead;
       // while(cur->next)
       // {
       //     cur = cur->next;
       // }
       // LinkedNode* node = new LinkedNode(val);
       // cur->next = node;
       // //node->next = nullptr;
       // _size++;
       LinkedNode* newNode = new LinkedNode(val);
       LinkedNode* cur = _dummyHead;
       while(cur->next != nullptr){
           cur = cur->next;
      }
       cur->next = newNode;
       _size++;
  }
   
   void addAtIndex(int index, int val) {
       if(index<0 || index> _size)return;//不能加等号,第_size的地方是可以被插入的
       LinkedNode* cur = _dummyHead;
       while(index--)
      {
           cur = cur->next;
      }
       LinkedNode* node = new LinkedNode(val);
       node->next = cur->next;
       cur->next = node;
       _size++;
  }
   
   void deleteAtIndex(int index) {
       // if(index<0)return;
       // if(index >= _size) return;
       // LinkedNode* cur = _dummyHead;
       // while(index--)cur = cur->next;
       // LinkedNode* tmp = cur->next;
       // cur->next = cur->next->next;
       // delete tmp;
       // tmp->next = nullptr;
       // _size--;
       if (index >= _size || index < 0) {
           return;
      }
       LinkedNode* cur = _dummyHead;
       while(index--) {
           cur = cur ->next;
      }
       LinkedNode* tmp = cur->next;
       cur->next = cur->next->next;
       delete tmp;
       //delete命令指示释放了tmp指针原本所指的那部分内存,
       //被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
       //如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
       //如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
       tmp=nullptr;
       _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);
*/

2.3 反转链表

思路1:双指针,实际上是三指针,还有一个是临时保存的指针,初始状态一个指向空pre,一个指向第一个元素cur,临时指针tmp指向第二个元素,向后迭代,直到cur为空反转完成

/**
* 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* pre = nullptr;
       ListNode* cur = head;
​
       while(cur)
      {
           ListNode* tmp = cur->next;
           cur->next = pre;
           pre = cur;
           cur = tmp;
      }
       return pre;
  }
};

思路2:递归的方法,递归子函数是reverse(pre,cur), 子函数的终止条件是cur走到空,整体思路是双指针的思路,终结过程是获取tmp,走一下,再重新cur,temp迭代,有点晦涩难懂

/**
* 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;
       return reverse(cur,tmp);
  }
   ListNode* reverseList(ListNode* head) {
       return reverse(nullptr,head);
  }
};

2.4 两两交换链表中的节点

需要补充一个虚拟头节点dummyHead, 交换两个节点需要三次连接步骤,需要保存第一和第三个节点,只要第一第二个节点不为空,就一直往后迭代,最后可以释放虚拟头(也可以不释放,写一下更好吧)

/**
* 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* swapPairs(ListNode* head) {
       //建立虚拟头节点
       ListNode* dummyHead = new ListNode(0);
       dummyHead->next = head;
       ListNode* cur = dummyHead;
​
       while(cur->next && cur->next->next)
      {
           //保存第一第三个节点
           ListNode* tmp = cur->next;
           ListNode* tmp1 = cur->next->next->next;
​
           //三次连接
           cur->next = cur->next->next;
           cur->next->next = tmp;
           tmp->next = tmp1;
​
           cur = tmp;
      }
​
       head = dummyHead->next;
       delete dummyHead;
       dummyHead = nullptr;
       return head;
  }
};

2.5 删除链表的倒数第N个节点

使用一个虚拟头节点,快慢指针都从dummyhead出发,删除倒数第n个,就先让快指针走n步,再一起走,结束条件是快指针的下一个是空的时候,删除慢指针的后一个元素即可,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* removeNthFromEnd(ListNode* head, int n) {
       ListNode* dummyHead = new ListNode(0);
       dummyHead->next = head;
       ListNode* slow = dummyHead;
       ListNode* fast = dummyHead;
​
       //先让fast走n步
       while(fast && n--)
      {
           fast = fast->next;
      }
       //一起走直到fast->next为空
       while(fast->next)
      {
           fast = fast->next;
           slow = slow->next;
      }
       //删除操作,删的是slow的后一个,c++记得要释放,所以要保存节点
       ListNode* tmp = slow->next;
       slow->next = slow->next->next;
       delete tmp;
       tmp = nullptr;
​
       return dummyHead->next;//dummyHead算法题一般不用删
  }
};

2.6 链表相交

先统计两条链表分别的长度len1和len2,计算长的减短的,让长的先走len2-len1步,再一起走,如果指针相同则找到,两个都走到空就没找到

/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
   ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
       int len1 = 0, len2 = 0;
       ListNode* p1 = headA;
       ListNode* p2 = headB;
       while(p1)
      {
           len1++;
           p1 = p1->next;
      }
       while(p2)
      {
           len2++;
           p2 = p2->next;
      }
       p1 = headA;
       p2 = headB;
       //让len1是长的那个
       if(len1 < len2)
      {
           swap(p1,p2);
           swap(len1,len2);
      }
​
       int sub = len1 - len2;
       //1先走sub步
       while(sub--)
      {
           p1 = p1->next;
      }
       while(p1 && p2)
      {
           if(p1 == p2) return p1;
           p1 = p1->next;
           p2 = p2->next;
      }
       return p1;
  }
};

2.7 环形链表 II

使用快慢指针来做,fast一次走两步,slow一次走一步,根据数学推理,如果有环,两个指针会在圈内的某一种处相遇,设一个index1指向相遇的位置,再定义一个index2指向head,一起走,由于数学公式,两者刚好相遇

(x + y) * 2 = x + y + n (y + z)
x = (n - 1) (y + z) + z
当 n为1的时候,公式就化解为 x = z,
/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
   ListNode *detectCycle(ListNode *head) {
       ListNode* fast = head,*slow = head;
       //因为fast一次走两个位置,所以fast和fast->next都不为空才可以
       while(fast && fast->next)
      {
           fast = fast->next->next;//快指针一次走两个位置
           slow = slow->next;
           if(slow == fast)//相遇
          {
               ListNode* index1 = slow;//index1保存相遇点
               ListNode* index2 = head;//index2保存head
               while(index1 != index2)
              {
                   index1 = index1->next;
                   index2 = index2->next;
              }
               return index1;
          }
      }
       return NULL;
       
  }
};