目录
对链表中常用的,虚拟头节点,增删改查,翻转,删除倒数节点,环形链表进行了介绍
用于单链表中删除指定元素,常用的方法包括:1虚拟节点操作,2原节点操作。
方法:
我们先以2原节点操作举例:
思路在于,我们需要做两次判断,第一种为被删除元素为头节点位置,第二种为非头节点位置。
头节点位置处理的思路是:
如果该头节点为目标值,那么我们让头节点的下一个节点为节点,
head = head->nex
之和我们在返回head头节点时,其实返回的是最开始的第二个节点,那么对于第一个节点,我们还需要释放内存。所以我们定义一个中间变量 tmp 保存 head 最开始的指针,当完成节点移动后,删除 tmp也就是删除了最开始的head。
- while(head != NULL && head->val == val){
- //需要删除头节点
- ListNode* tmp = head;
- head = head->next;//头节点在第二个节点位置了
- delete tmp;//释放空间最开始的头节点
- }
其他节点位置处理的思路是:
由于当前被删除的节点必不可能为头节点,因此删除的思路是,如果该节点需要被删除,那么就让该节点的上一个节点指向被删除节点的下一个节点。同时也需要定义中间变量进行释放内存。因此整个代码如下:
- /**
- * 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 != NULL && head->val == val){
- //需要删除头节点
- ListNode* tmp = head;
- head = head->next;//头节点在第二个节点位置了
- delete tmp;//释放空间最开始的头节点
- }
- //删除非头节点
- ListNode* cur = head;
- while(cur != NULL && cur->next != NULL){
- //数组还在边界内
- if(cur->next->val == val){
- ListNode* tmp = cur->next;
- cur->next = cur->next->next;//让cur指向删除的位置指向被删除位置的下一个
- delete tmp;//释放空间被删除的节点
- }
- else{
- cur = cur->next;
- }
- }
- return head;
- }
- };
我们以1虚拟节点操作举例:
这里的思路是:先建立一个虚拟节点,该节点指向第一个节点,那么在后续判断中就不需要再判断头节点了,直接按照删除非头节点的方式。
- ListNode* dummyHead = new ListNode(0);//设置虚拟节点
- dummyHead->next = head;
- ListNode* cur = 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* removeElements(ListNode* head, int val) {
- ListNode* dummyHead = new ListNode(0);//设置虚拟节点
- dummyHead->next = head;
- ListNode* cur = dummyHead;
- while(cur->next != NULL){
- //在有效区间内
- 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;
- }
- };
相关题目:
203. 移除链表元素 - 力扣(LeetCode)
https://leetcode.cn/problems/remove-linked-list-elements/
对单链表进行头插,尾插,任意插,任意删,获取任意节点的值。我们将分别介绍这几个部分
方法:
头插:
定义一个虚拟节点指向头节点,定义一个新节点。同时,新节点指向头节点,虚拟节点指向新节点
- void addAtHead(int val) {
- LinkNode *newNode = new LinkNode(val);
- newNode->next = _dummyHead->next;//把新节点指向头节点
- _dummyHead->next = newNode;//把虚拟节点指向新节点
- _size++;//节点数量增加
- }
尾插:
定义一个指针指向虚拟节点,定义一个新节点被最后位置指向
- void addAtTail(int val) {
- LinkNode* newNode = new LinkNode(val);
- LinkNode* cur = _dummyHead;
- //这里cur不一样可以试着代值进去
- while(cur->next != nullptr){
- cur = cur->next;
- }
- cur->next = newNode;
- _size++;
- }
任意插:
- void addAtIndex(int index, int val) {
- if(index > _size || index < 0){
- return ;
- }
- LinkNode* newNode = new LinkNode(val);
- LinkNode* 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 ;
- }
- LinkNode* cur =_dummyHead;
- while(index--){
- cur = cur->next;
- }
- LinkNode* tmp = cur->next;
- cur->next = cur->next->next;
- delete tmp;
- _size--;
- }
相关题目:
707. 设计链表 - 力扣(LeetCode)
https://leetcode.cn/problems/design-linked-list/
原理:
对于单链表进行元素翻转时,我们利用双指针方法,分别指向前后节点,交换节点指向顺序实现整个链表的翻转
方法:
包括:1双指针法,2递归法实现,两者思路一样,需要注意的是,在翻转之后移动指针时,先移动后指针再移动前指针。
我们现在以双指针举例:
定义一个cur指向前节点赋值为head,定义一个后节点赋值为NULL。因为翻转后,之前的头节点需要指向NULL
- ListNode* cur = head;//后节点
- ListNode* pre = NULL;//前节点
当前节点指向NULL,也就是之前的尾节点时结束循环,同时利用一个中间变量保存cur的下一个节点,因为我们需要把cur的下一个节点赋值给pre,没有中间变量将导致找不到下一个节点了。当完成一次翻转后,后节点,前节点依次前移
- class Solution {
- public:
- ListNode* reverseList(ListNode* head) {
- ListNode* cur = head;//后节点
- ListNode* pre = NULL;//前节点
- while(cur){
- //当cur指向空结束
- ListNode* temp = cur->next;//保留cur之前的下一个节点
- cur->next = pre;//指针翻转;
- //指针同时前移,先移pre,再cur
- pre = cur;
- cur = temp;
- }
- return pre;
- }
- };
我们现在以递归举例:
由双指针思路一样,我们递归函数为reverse(),其中传入为reverse(head,NULL),也就是之前的(cur,pre),然后返回pre
return reverse(head,NULL);
当cur=NULL时结束,返回pre
- if(cur == NULL){
- //结束
- return pre;
- }
定义中间变量和翻转节点,这里和双指针一样
- ListNode* temp = cur->next;
- cur->next = pre;//翻转指针
现在前移节点,按照双指针,先移动pre,再移动cur,因此传入的reverse按照这个样子,总代码如下:
- class Solution {
- public:
- ListNode* reverse(ListNode* cur,ListNode* pre){
- if(cur == NULL){
- //结束
- return pre;
- }
- ListNode* temp = cur->next;
- cur->next = pre;//翻转指针
- //移动指针
- return reverse(temp, cur);
- }
- ListNode* reverseList(ListNode* head) {
- return reverse(head,NULL);//cur,pre的顺序传入,返回pre
-
- }
- };
相关题目:
206. 反转链表 - 力扣(LeetCode)
https://leetcode.cn/problems/reverse-linked-list/

创建虚拟头节点指向头节点,定义一个指针指向虚拟头节点,以及保存 1, 3节点的指针
- ListNode* dummyHead = new ListNode(0);//虚拟头节点
- dummyHead->next = head;
- ListNode* cur = dummyHead;
- //1.保存 1,3节点
- ListNode* temp1 = cur->next;
- ListNode* temp2 = cur->next->next->next;
判断链表遍历结束,一定要先判断cur->next。否则出现空指针报错
while(cur->next != nullptr && cur->next->next != nullptr){
//1.交换cur 和 2
cur->next = cur->next->next;
//2.交换1,2
cur->next->next = temp1;
//3.交换1,3
temp1->next = temp2;
//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* swapPairs(ListNode* head) {
- ListNode* dummyHead = new ListNode(0);//虚拟头节点
- dummyHead->next = head;
- ListNode* cur = dummyHead;
- while(cur->next != nullptr && cur->next->next != nullptr){
- //1.保存 1,3节点
- ListNode* temp1 = cur->next;
- ListNode* temp2 = cur->next->next->next;
- //1.交换cur 和 2
- cur->next = cur->next->next;
- //2.交换1,2
- cur->next->next = temp1;
- //3.交换1,3
- temp1->next = temp2;
- //cur移动两个
- cur = cur->next->next;
- }
- return dummyHead->next;
- }
- };
相关题目:
24. 两两交换链表中的节点 - 力扣(LeetCode)
https://leetcode.cn/problems/swap-nodes-in-pairs/
方法:
要删除链表中的倒数第n个节点,我们使用:双指针方法。
定义一个虚拟头节点指向head,两个快慢指针指向虚拟节点
- ListNode* dummyHead = new ListNode(0);
- dummyHead->next = head;
- ListNode* fast = dummyHead;
- ListNode* slow = dummyHead;
先让快指针移动n+1个位置,再让快慢指针一起移动,当快指针到了NULL时,此时慢指针在被删除元素的前一个位置。
- while(n-- && fast != NULL){
- fast = fast->next;
- //先让fast移动n
- }
- //fast移动再+1,这样slow在被删除元素前一个
- fast = fast->next;
- //fast slow一起移动,直到fast到NULL
- while(fast != NULL){
- fast = fast->next;
- slow = slow->next;
- }
现在进行元素删除,让slow的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* removeNthFromEnd(ListNode* head, int n) {
- ListNode* dummyHead = new ListNode(0);
- dummyHead->next = head;
- ListNode* fast = dummyHead;
- ListNode* slow = dummyHead;
- while(n-- && fast != NULL){
- fast = fast->next;
- //先让fast移动n
- }
- //fast移动再+1,这样slow在被删除元素前一个
- fast = fast->next;
- //fast slow一起移动,直到fast到NULL
- while(fast != NULL){
- fast = fast->next;
- slow = slow->next;
- }
- ListNode* temp = slow->next;
- slow->next = slow->next->next;
- delete temp;
- return dummyHead->next;
- }
-
- };
相关题目:
19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
方法:
求两个链表相交的位置,如果没有就返回空,我们利用一个巧妙办法,先让比较长的链表的头指针移动两个链表的差距长度,然后以长链表开始,遍历两个链表的节点是否一样。
- /**
- * 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) {
- ListNode* curA = headA;
- ListNode* curB = headB;
- int lenA = 0, lenB = 0;
- //求A,B链表长度
- while(curA != NULL){
- lenA++;
- curA = curA->next;
- }
- while(curB != NULL){
- lenB++;
- curB = curB->next;
- }
- //让curA,curB回到头节点
- curA = headA;
- curB = headB;
- //找到最长链表,设置为A
- if(lenB > lenA){
- swap(lenA,lenB);
- swap(curA,curB);
- }
- //求差距长度
- int gap = lenA - lenB;
- while(gap--){
- curA = curA->next;
- //curA移动gap个长度
- }
- //遍历A,B是否有相同的
- while(curA != NULL){
- if(curA == curB){
- return curA;
- }
- curA = curA->next;
- curB = curB->next;
- }
- return NULL;
- }
- };
相关题目:
面试题 02.07. 链表相交 - 力扣(LeetCode)
https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/
方法:
利用双指针实现,题目满足当快慢指针相遇后,同时移动z个距离必定相遇。
根据这个要求:我们做如下工作:
让快慢指针一起移动,并且满足在边界范围内
- while(fast != NULL && fast->next != NULL){
- fast = fast->next->next;
- slow = slow->next;
- }
当移动过程中两者相遇,则让两者继续移动,两者必定会碰到
- if(fast == slow){
- ListNode* index1 = head;
- ListNode* index2 = fast;
- while(index1 != index2){
- //直到两者在移动z个距离碰到
- index1 = index1->next;
- index2 = index2->next;
- }
完整代码如下:
- /**
- * 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;
- ListNode* slow = head;
- while(fast != NULL && fast->next != NULL){
- fast = fast->next->next;
- slow = slow->next;
- //一直移动快慢指针,直到相遇
- if(fast == slow){
- ListNode* index1 = head;
- ListNode* index2 = fast;
- while(index1 != index2){
- //直到两者在移动z个距离碰到
- index1 = index1->next;
- index2 = index2->next;
- }
- return index1;
- }
- }
- return NULL;
-
- }
- };
相关题目:
142. 环形链表 II - 力扣(LeetCode)
https://leetcode.cn/problems/linked-list-cycle-ii/