文章目录
- 1.[环形链表II](https://leetcode.cn/problems/linked-list-cycle-ii/description/)
- 1.1 题目要求
- 1.2 快慢指针
- 1.3 哈希法
- 2.[随机链表的复制](https://leetcode.cn/problems/copy-list-with-random-pointer/description/)
- 2.1 题目要求
- 2.2 迭代法
1.环形链表II
1.1 题目要求
找到环形链表的入口并返回该节点,如果找不到就返回NULL。
1.2 快慢指针
在话 环形链表I中我们就用到了,快慢指针来判断一个链表中是否存在环。在环形链表I中已经解释了为什么会相遇。可是现在的问题是找到环的入口,我们的快慢指针好像做不到吧。
别急嘛,下面我将用数学的方式来证明可以做到!
数学证明
初始时,快慢指针都在链表的起始位置,在指针开始运动前我们要了解,快指针的速度的慢指针的两倍,也就说明了快指针的运动的路程是慢指针的两倍。
定义快指针为红色箭头,慢指针为蓝色箭头。
运动开始,当运动到慢指针刚好进入到环中时,快指针已经在环中运动了k圈了(k>=0)
此时的慢指针在A点,快指针在B点。AB距离为x,BA距离为y慢指针运动路程为z
,快指针运动距离为k*(x+y)+x
根据快指针的运动路程是慢指针的两倍可得
2*z = k*(x+y)+x;
化简为
z = k*(x+y)+x
将图中的z替换得:
下面就是快慢指针相遇时刻,根据图中得距离BA为y,因为快指针的速度是慢指针的两倍,那么就说明了它们的相遇时刻是慢指针再运动y距离的时刻,此时的快指针运动了2y
现在它们相遇了,从图中观察,AC的距离为x。然后环的大小为x+y,以及从head到A的距离为x+k(x+y),这不就说明了,我们只要派出一个慢指针从head运动到A不就可以了吗,让环慢指针在环中转k圈,环外的慢指针也运动了k(x+y)的距离,此时它们离A都只差x,同时速度又是一样,由此两个慢指针是会在A点相遇的。
证明过程中的图片来源:Shawxing精讲算法
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* fast = head;
struct ListNode* slow =head;
while(fast&&fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(slow == fast)
{
struct ListNode* cur = head;
while(cur!=slow)
{
cur = cur->next;
slow = slow->next;
}
if(cur==slow)
return cur;
}
}
return NULL;
}
1.3 哈希法
利用哈希表unordered_map来存储链表的每个节点,链表存在环时,一定会有重复的节点进入哈希表,我们只要关注第一个重复加入哈希表的节点即可。
用c写很麻烦,代码我用c++写的。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_map<ListNode*,bool> cnt;
ListNode* cur = head;
while(cur)
{
if(cnt.find(cur)!=cnt.end())
{
return cur;
}
cnt[cur] = true;
cur = cur->next;
}
return nullptr;
}
};
2.随机链表的复制
2.1 题目要求
创建一个新链表,这个新链表所有的节点、next链接和random链接都要与原链表完全相同,返回新链表的头。
2.2 迭代法
先创建和原链表值相同的节点,让原链表中的节点指向新创建的节点前,用新创建的节点指向原节点的下一个节点。
处理random
在把cur指向head,重新遍历链表,因为原本的链表因为新节点的加入有所改变,当时我们还是要让cur每次都指向原链表的节点,再创建一个copy指向新加入的链表。
因为一一配对的原因,我们只需要让copy节点指向原先节点指向的random的下一个节点就可以了就可以,不过有个例外就指向NULL的节点,特别判断一下就可以了
链接copy节点
把copy指向原链表节点的指针统统指向copy节点就可以了
struct Node* copyRandomList(struct Node* head) {
struct Node* cur = head;
while(cur)
{
struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
copy->val = cur->val;
copy->next = cur->next;
cur->next = copy;
copy->random = NULL;
cur = copy->next;
}
cur = head;
while(cur)
{
struct Node* copy = cur->next;
struct Node* next = copy->next;
if(cur->random == NULL)
copy->random = NULL;
else
copy->random = cur->random->next;
cur = next;
}
//链接copy节点
struct Node* phead = NULL;
struct Node* tail = NULL;
cur = head;
while(cur)
{
struct Node* copy = cur->next;
struct Node* next = copy->next;
if(phead == NULL)
{
phead = copy;
tail = phead;
}
else
{
tail->next = copy;
tail = copy;
}
cur = next;
}
return phead;
}
那么关于链表题目的讲解就先到此为止了,如果大家对链表的题目还是很感兴趣下去以后可以刷这里的题:力扣链表和牛客链表的题目
完