24. 两两交换链表中的节点
题目链接:24. 两两交换链表中的节点
文档讲解:代码随想录
思路
首先为便于处理头结点,设置一个虚拟头结点dummyHead。设置一个cur
指针指向需要交换结点的前驱结点,当cur->next
和cur->next->next
都存在时说明后续两个结点需要交换,此时利用临时结点变量记录好结点后,完成交换操作。
代码
class Solution
{
public:
ListNode *swapPairs(ListNode *head)
{
ListNode *dummyHead = new ListNode(0, head);
ListNode* cur = dummyHead;
while (cur->next && cur->next->next)
{
ListNode* temp1 = cur->next;
ListNode* temp2 = cur->next->next->next;
cur->next = temp1->next;
cur->next->next = temp1;
temp1->next = temp2;
cur = cur->next->next;
}
return dummyHead->next;
}
};
19. 删除链表的倒数第N个节点
题目链接:19. 删除链表的倒数第 N 个结点
文档讲解:代码随想录
思路
删除链表的结点时,需要知道所删除结点的位置,若知道所删除结点据头结点head的位置,则一次遍历至所删除结点的前驱结点,然后完成删除操作;若直到所删除结点据尾结点的位置N,则可以通过两个相距N的指针,将后指针指向尾结点,则前指针指向所删除结点的前驱结点,然后完成删除操作。
代码
class Solution
{
public:
ListNode *removeNthFromEnd(ListNode *head, int n)
{
ListNode *dummyHead = new ListNode(0, head);
ListNode *pre = dummyHead;
ListNode *cur = dummyHead->next;
ListNode *beh = cur;
n--;
while (n--)
{
beh = beh->next;
}
while (beh->next)
{
pre = pre->next;
cur = cur->next;
beh = beh->next;
}
ListNode *temp = cur;
pre->next = cur->next;
delete temp;
return dummyHead->next;
}
};
相关题目
26. 删除有序数组中的重复项
283. 移动零
844. 比较含退格的字符串
977. 有序数组的平方
面试题 02.07. 链表相交
题目链接:面试题 02.07. 链表相交
文档讲解:代码随想录
思路
两个链表相交指的是两个链表的结点地址相同,而不是值相同,如下图中实际相交结点为8,而不是1。
链表相交的思路是设置两个指针分别指向两个链表相交结点前的相同位置,如下图中的4和0,这样让两个指针分别向后走,即可找到相交结点。
所以首先要求出链表B的0位置,链表B和链表A的长度差就是链表B的0相对于head结点的位置,因此可以先分别遍历两个链表,求出两个链表的长度差l,然后设置一个结点指向链表B的头结点,向后走l个,这样链表A和链表B的结点就对齐了。
class Solution
{
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
{
int len1 = 0;
int len2 = 0;
ListNode *dummyHead1 = new ListNode(0, headA);
ListNode *dummyHead2 = new ListNode(0, headB);
ListNode *p = dummyHead1;
while (p->next)
{
p = p->next;
len1++;
}
p = dummyHead2;
while (p->next)
{
p = p->next;
len2++;
}
if (len1 < len2)
return getIntersectionNode(headB, headA);
int gap = len1 - len2;
p = headA;
ListNode *q = headB;
while (gap--)
p = p->next;
while (p && q && p != q)
{
p = p->next;
q = q->next;
}
if (p && q && p == q)
return p;
else
return NULL;
}
};
142. 环形链表II
题目链接:142. 环形链表 II
文档讲解:代码随想录
视频讲解:把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表II
思路
首先要判断该链表是否有环,然后再找到环的入口。
使用快慢双指针法,慢指针一次走一个结点,快指针一次走两个结点,如果快慢指针相遇(即指向同一结点),则该链表有环,否则链表没有环。
当链表有环时,找到该环的入口是这道题的难点,需要推导一下。
如上图,假设链表的头结点至环入口的结点数为
x
x
x,环入口至快慢指针相遇点的结点数为
y
y
y,快慢指针相遇点至环入口的结点数为
z
z
z。
则快慢指针相遇时,慢指针走了 x + y x+y x+y个结点,快指针走了 x + y + n ( y + z ) x+y+n(y+z) x+y+n(y+z)个结点,其中 n n n代表圈数。
因为快指针的速度是满指针的两倍,故有 x + y + n ( y + z ) = 2 ( x + y ) x+y+n(y+z) = 2(x+y) x+y+n(y+z)=2(x+y)
整理得到 x = z + ( n − 1 ) ( y + z ) x=z+(n-1)(y+z) x=z+(n−1)(y+z)
上式表示,环入口至头结点的距离 x x x等于 ( n − 1 ) (n-1) (n−1)圈环加快慢指针相遇点至环入口的结点数。
因此设置一个指针1指向头结点,一个指针2指向快慢指针相遇点,两个指针同步向后走,指针2在环中绕 n − 1 n-1 n−1圈后再走 z z z个结点就与指针1在环入口相遇。
代码
class Solution
{
public:
ListNode *detectCycle(ListNode *head)
{
if (!head || !head->next)
return NULL;
ListNode *slow = head;
ListNode *fast = head;
ListNode *start = head;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
break;
}
if (fast == slow)
{
while (fast != start)
{
start = start->next;
fast = fast->next;
}
return start;
}
else
{
return NULL;
}
}
};
总结
算法训练营中关于链表的问题今天就结束了。链表问题最常用的方法是双指针法,必须要明确两个指针的含义,同时要处理好指针的边界。