1、给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
解法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)//空链表没有环
return false;
ListNode *fast = head,*slow = head;//定义快慢指针指向头结点
while(fast != NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
if(fast == slow)
return true;//指针相等说明有环
}
return false;//否则无环
}
};
示例:
输入:head=[3,2,0,4]
输出:true
2、给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。
解析:
设置快指针、慢指针,若链表有环,则两个指针一定相遇,此时头指针head和快慢指针的距离x与快慢指针到入环第一个节点的距离y相等或成倍数关系。故将fast指针重新指向head,fast和slow指针一起每次只走一步,直到fast==slow时,快慢指针恰好在入环的第一个节点,返回指针即可
/**
* 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) {
if(head == NULL)
return NULL;
ListNode *fast = head,*slow = head;
while(fast != NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
{
fast = head;//快指针返回到头结点
while(fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
}
return NULL;
}
};
3、找出并返回两个单链表相交的起始节点
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。注意,函数返回结果后,链表必须 保持其原始结构 。
解法1:固定指针p,遍历另一链表
指针p、pre分别指向headA和headB,固定指针p,pre遍历链表,若p和pre指向同一节点,返回指针p,否则pre重新指向headB,p指向下一节点,继续遍历(复杂)
/**
* 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) {
if(headA == NULL || headB == NULL)
return NULL;
ListNode *p = headA,*pre = headB;//分别指向headA和headB
while(p != NULL)
{
pre = headB;//pre遍历完后重新指向headB
while(pre != NULL)
{
if(p == pre)
return p;
else
pre = pre->next;
}
p = p->next;
}
return NULL;
}
};
解法2:先统计链表长度
若长度相等,p和pre同时出发,直到到达相同节点;若长度不同,长链表的指针先出发直到长度相等,另一指针再出发
class Solution {
/*统计链表的长度的函数*/
private:int length(ListNode *node)
{
int length = 0;
while (node != NULL)
{
node = node->next;
length++;
}
return length;
}
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int lenA = length(headA),lenB = length(headB);//统计链表A和链表B的长度
while (lenA != lenB)
{
if(lenA > lenB) //如果链表A长,那么链表A先走
{
headA = headA->next;
lenA--;
}
else
{
headB = headB->next;//如果链表B长,那么链表B先走
lenB--;
}
}
while (headA != headB)//然后开始比较,如果他俩不相等就一直往下走
{
headA = headA->next;
headB = headB->next;
}
return headA;//走到最后,最终会有两种可能,一种是headA为空,
//也就是说他们俩不相交。还有一种可能就是headA
//不为空,也就是说headA就是他们的交点
}
};
解法3:双指针交替
两指针同时出发,相遇于同一节点就返回节点,否则,若p走完A链表,p变轨指向B链表,继续出发,pre走完B链表则变轨指向A链表,直到相遇
相遇时所走路程:p:S1+S3+S2
pre:S2+S3+S1
故一定会相遇(p和pre最多能变轨一次)
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *p = headA,*pre = headB;
//变轨次数:p变一次,pre变一次,cnt大于等于2则返回NULL
int cnt=0;
while (p != NULL && pre != NULL)
{
if (p == pre)
return p;
p = p->next;
pre = pre->next;
//p变轨
if(cnt < 2 && p == NULL)
{
p = headB;
cnt++;
}
//p2变轨
if(cnt < 2 && pre == NULL)
{
pre = headA;
cnt++;
}
}
return NULL;
}
};
4、
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
解法:快慢指针
快指针先走n步,之后快慢指针一起走,快指针到终点时慢指针恰好到达倒数din个结点的位置
/**
* 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 *p = head,*pre = head;//快指针p,慢指针pre
for(int i = 0;i < n;i++)//p先移动n位
p = p->next;
if(p == NULL)//要删除的是头结点
return head->next;
while(p->next != NULL)//while循环结束时pre恰好指向要删除节点的前一位
{
p = p->next;
pre = pre->next;
}
pre->next = pre->next->next;//覆盖
return head;
}
};