对于链表的题目真的是超级多,想要每道题都弄懂其实不难,只要掌握方法,掌握思想,做好边界检查,弄清每道题的题意,那么就可以举一反三,也不那么容易写出崩溃的代码。
对于如何判断一个链表带环这个问题,可以这样来分析它
如图是一个带环的链表,首先可以使用两个指针同指向链表的第一个节点,其中一个叫slow,另一个叫fast。
其次让slow这个指针每次走一步,fast每次走两步,图中每个指针旁边的数字代表它所走的第几步,可以看见,如果一个链表带环,那么这两个指针便会相遇,当然下面这个图很巧的是刚好是在环的入口相遇,但是计算机是不会知道这就是环的入口,所以需要写出代码来告诉计算机该链表是有环的以及环的入口在哪里。(不是带环的链表两个指针一定就会相遇在环的入口,但是一定是会相遇的,有点绕但是要搞清)
/*struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) { } }; */
// 该段程序是判断该链表中是否有环,如果有则返回相遇的节点,没有返回空 ListNode* MeetingNode(ListNode* pHead) { if(pHead==NULL) return NULL; ListNode* slow = pHead->next; if(slow==NULL) return NULL;//判断慢指针是否为空 ListNode* fast = slow->next; if(fast==NULL) return NULL;//判断快指针是否为空 while(slow!=NULL&&fast!=NULL) { if(slow==fast) return fast;//两个指针相等则说明有环返回快指针 slow = slow->next;//慢指针指向下一个,为什么不用判断,因为快指针已经走过,肯定不为空 fast = fast->next;//不能直接向后走两个,因为可能下两个为空,则会崩溃,所以下面要判断 if(fast->next!=NULL) fast = fast->next; } return NULL;//如果没有相等的快慢指针则直接返回空 }
求环的入口步骤
先将上一步返回的指针记录下来,然后判断环里有几个结点,让p2先走环里结点的步数(p2旁边的数字即是)再让p1和p2步数,那么两个指针相遇的地方就是环的入口。
//该段程序是求环的入口 class Solution { public: ListNode* EntryNodeOfLoop(ListNode* pHead) { ListNode* pNode = MeetingNode(pHead); if(pNode==NULL) { return NULL; } int count = 1; ListNode* cur = pNode;//先记录出环里有几个节点 while(cur->next!=pNode) { ++count; cur = cur->next; } //然后让一个指针先走环里节点的步数,再让两个指针一起走,相遇的节点就是环的入口 ListNode* p1 = pHead; ListNode* p2 = pHead; for(int i = 0;i<count;i++) { p2 = p2->next; } while(p1!=p2) { p1 = p1->next; p2 = p2->next; } return p2; } };