目录
1.判断链表是否带环
证明
2.寻找环的入口点
1.判断链表是否带环
原题链接:力扣
思路一:先遍历一遍链表,计算出链表的长度,然后将长度除二,在遍历半个链表即可。但是这种方法效率比较低。
思路二:使用快慢指针,慢指针每次走1步,快指针每次走2步,当快指针走到末尾是,慢指针的位置就是链表中间位置。
下面我们使用思路二来完成题目:
bool hasCycle(struct ListNode* head)
{struct ListNode* slow = head;struct ListNode* fast = head;while (fast && fast->next){slow = slow->next;fast = fast->next->next;if (fast == slow){return true;}}return false;
}
证明
1.为什么快指针每次走两步,慢指针走一步可以追上?
2.快指针一次走3步,走4步,...n步行吗?
证明:假设slow进环以后,fast和slow的距离是N
这时fast真正开始追击slow,fast一次走2步,slow一次走1步,他们之间的距离每次缩小1
假设slow进环以后,fast和slow的距离是N
这时fast真正开始追击slow,fast一次走3步,slow一次走1步,他们之间的距离每次缩小2
这时候要分情况讨论:
2.寻找环的入口点
原题链接:力扣
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改链表。
方法一:用一个指针从链表的头节点出发,另一个指针从链表相遇的节点出发,当这两个指针相遇时,说明它们到达了入口
下面是原理:
代码实现:
struct ListNode *detectCycle(struct ListNode *head)
{struct ListNode* slow = head;struct ListNode* fast = head;while (fast && fast->next){slow = slow->next;fast = fast->next->next;if (fast == slow){struct ListNode *meet=slow;while(meet!=head){meet=meet->next;head=head->next;}return meet;}}//如果链表没有环就返回空return NULL;
}
方法二:可以将这个题转化成两个链表相交的问题。
将meet的下一个节点设置成newHead,求出head到meet的长度n, newHead到meet的长度m, 再利用快慢指针,使长的链表先走fabs(m-n)次,再同时走,当两个指针想等时,就到达了环的入口。
代码实现:
struct ListNode* detectCycle(struct ListNode* head)
{struct ListNode* slow = head;struct ListNode* fast = head;while (fast && fast->next){slow = slow->next;fast = fast->next->next;if (slow == fast){struct ListNode* meet = slow;struct ListNode* moveA = slow;struct ListNode* moveB = head;//计算两链表的长度int lenA = 0;int lenB = 0;while (moveA != meet){moveA = moveA->next;lenA++;}while (moveB != slow ){moveB = moveB->next;lenB++;}//计算长度差int gab = fabs(lenA - lenB);struct ListNode* longList = head;struct ListNode* shortList = slow;if (lenA < lenB){longList = slow;shortList = head;}//让长的链表先走while (gab--){longList = longList->next;}while (longList != shortList){longList = longList->next;shortList = shortList->next;}return longList;}}//不相交返回空return NULL;
}
很明显的看出来,使用方法二的代码比方法一要复杂的多