目录
前言
LeetCode24.两两交换链表中的结点
LeetCode19.删除链表的倒数第N个结点
LeetCode面试题02.07. 链表相交
LeetCode142.环形链表Ⅱ
一、LeetCode24 两两交换链表中的结点
题目链接:两两交换链表中的结点
设置虚拟头结点代码:
/**
* 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 *newHead = new ListNode(0, head);
ListNode *pFir = nullptr;
ListNode *pPre = newHead;
while(pPre->next && pPre->next->next)
{
pFir = pPre->next;
pPre->next = pPre->next->next;
pFir->next = pFir->next->next;
pPre->next->next = pFir;
pPre = pPre->next->next;
}
return newHead->next;
}
};
代码思路:
设置一个虚拟头结点newHead, 然后根据下图指针修改流程来循环修改指针
结点指针修改流程:
二、LeetCode19 删除链表的倒数第N个结点
题目链接:删除链表的倒数第N个结点
双指针法:
/**
* 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 *newHead = new ListNode(0, head);
ListNode *fast = newHead;
ListNode *slow = newHead;
while(n-- && fast->next)
{
fast = fast->next;
}
if(n >= 0)
return nullptr;
while(fast->next)
{
slow = slow->next;
fast = fast->next;
}
ListNode *delNode = slow->next;
slow->next = delNode->next;
delete delNode;
return newHead->next;
}
};
代码思路:
设置快慢两个指针,如果要找倒数第n个结点,首先让快指针从newHead指针处先向后移动n个结点,然后slow和fast同时移动,直到fast的下一个结点为NULL时,此时slow的下一个结点就是我们要找的倒数第n个结点。假设我们要找倒数第3个结点,详细的流程如下图:
首先移动fast指针:
再同时移动fast和slow指针:
最后就是通过slow指针进行正常的删除操作即可。
三、LeetCode面试题 链表相交
题目链接:链表相交
双指针法:
/**
* 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;
int lenB = 0;
while(curA)
{
curA = curA->next;
lenA++;
}
while(curB)
{
curB = curB->next;
lenB++;
}
curA = headA;
curB = headB;
// 让curA指向比较长的链表
if(lenA < lenB)
{
swap(curA, curB);
swap(lenA, lenB);
}
for(int i = 0; i < lenA - lenB; i++)
{
curA = curA->next;
}
while(curA)
{
if(curA == curB)
{
return curA;
}
curA = curA->next;
curB = curB->next;
}
return nullptr;
}
};
代码思路:
如果链表A和链表B存在相交,那么A和B链表的倒数几个结点必然是一样的。所以先得到A和B的链表长度lenA和lenB(此时假设lenA>lenB)。然后让链表A的结点向后移动lenA - lenB个结点,最后开始同时遍历,直到找到相同的结点或者遍历到链表末尾为止。
四、LeetCode142 环形链表Ⅱ
题目链接:环形链表Ⅱ
双指针法代码:
/**
* 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 && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
{
slow = head;
while(fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return fast;
}
}
return nullptr;
}
};
代码思路:
如下图,假设链表有环,链表的直线部分长度为x,从环的入口处到fast和slow指针相交处长度为y,从相交处回到环的入口处的长度为z。
可得快指针fast走过的长度为:(其中n代表走过的环形链表的圈数)
慢指针slow的走过的长度:
联立两个式子,由于是
的两倍,所以有
可得:
再化简进行加减消元:
该式子还可以转换为:(这个式子就是最重要的关系式)
通过最后的关系式,我们可以得知,从fast和slow相遇的位置开始移动n - 1圈再加上距离z,刚刚好就等于x的距离,所以此时,我们只要再让一个新的指针从head出发,当前指针从相交点出发,不断往前移动一格,最终一定会再环的入口处相交!
总结:
在涉及到单链表的删除或者查找的时候,可以优先想一想是否能采用双指针的办法来锁定到要求的结点,通过双指针法可以减少遍历的次数,得到更小的时间复杂度。