来源自:https://leetcode-cn.com/leetbook/read/linked-list/jbex5/
1)链表中的双指针
我们在链表中使用两个速度不同的指针时会遇到的情况:
1.如果没有环,快指针将停在链表的末尾。
2.如果有环,快指针最终将与慢指针相遇。
所以剩下的问题是:
这两个指针的适当速度应该是多少?
一个安全的选择是每次移动慢指针一步,而移动快指针两步。每一次迭代,快速指针将额外移动一步。如果环的长度为 M,经过 M 次迭代后,快指针肯定会多绕环一周,并赶上慢指针。
2)环形链表(一)
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是
-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
题解1(官方题解):
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
//判断单个结点的情况
if(head==NULL||head->next==NULL)
return false;
//设置快指针比慢指针快一步
ListNode* low=head;
ListNode* fast=head->next;
//如果没有环,fast指针总是会比low指针更快到链表末尾
while(low!=fast)
{
if(fast==NULL||fast->next==NULL)
return false;
low=low->next;
fast=fast->next->next;
}
return true;
}
};
3)环形链表2
题目:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是
-1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
题解1(利用哈希表):
思路:set容器通常用来有序而唯一的存放元素,而unorder_set容器可以无序而唯一地存放元素。在这里我们可以利用unorder_set容器构建一个哈希表,并利用其中的count()方法来判断元素存在的个数(因为是唯一的,其实只能为0或者1)。
刚开始访问时,count()都为0,因此需要先判断,再插入元素;当count()为1时,则说明某个元素被重复访问到了,该元素即为链表尾巴连接到链表的位置。
/**
* 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) {
//无序set构造哈希表
unordered_set<ListNode*> discord;
while(head!=NULL)
{
//在第一次访问时,先判断,再将结点插入进去
//如果有某个结点被二次访问,则说明有环,并返回该结点
if(discord.count(head))
return head;
discord.insert(head);
head=head->next;
}
return NULL;
}
};
题解2(利用快慢指针):
思路:其实就是一个简单的追赶问题,通过画个图很快就可以的出来。假如有环的时候,快慢指针相遇的点走到连接点和从头指针走向连接点的距离是相等的。因此在相遇后,可以再设置一个从头指针开始的指针,和low指针一起走,最后两个相遇到的结点就是连接点了。
PS:在判断fast指针及其后继是否为空的时候,一直报错。其错误原因是我把fast->next==null这一条件放在了前面。因为或运算是从左到右的,而其中其实可能fast就为null,则fast->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,*low,*tmp;
fast=low=tmp=head;
if(head==NULL||head->next==NULL)
return NULL;
do{
if(fast==NULL||fast->next==NULL)
return NULL;
fast=fast->next->next;
low=low->next;
}while(fast!=low);
while(low!=tmp)
{
low=low->next;
tmp=tmp->next;
}
return tmp;
}
};
4)相交链表
题解1(暴力解法):
思路:遍历一链表的每一个节点,同时与另外一条链表的每一个节点作比较。
代码实现还是比较简单的,每次遍历链表A的单个节点后,与链表B的每一个节点进行对比,这样时间复杂度很高。
题解1(用哈希表):
思路:先遍历一条链表,将每一个节点放入哈希表中。然后再遍历第二条链表,第一个重复出现的节点就是两个链表的相交点了。这种解法虽然简单易懂,但是用时很多。
/**
* 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) {
unordered_set<ListNode *> visited;
if(headA==NULL||headB==NULL)
return NULL;
while(headA!=NULL)
{
visited.insert(headA);
headA=headA->next;
}
while(headB!=NULL)
{
if(visited.count(headB))
return headB;
headB=headB->next;
}
return NULL;
}
};
题解2(双指针):
思路: