【题目描述】
输入一个链表,输出该链表中倒数第k个结点。
【解题思路】
为了实现只遍历一次就能找到倒数第k个节点,我们可以定义两个指针(俗称“快慢指针”)。第一个指针从链表的头结点开始遍历向前走k-1,第二个指针保持不动;从第k步开始,第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离始终保持在k-1,当第一个指针到达链表的末尾时,第二个指针刚好走到倒数第k个节点。
理解:两个指针的间距从开始位置处随着指针的移动到达末尾位置一直保持在k-1,这样当尾指针到达末尾成为倒数第一个节点的时候,k-1位置的节点刚好成为倒数第k个节点。
注意:
快慢指针
注意边界和异常条件判断
【代码实现】
1 /* 2 struct ListNode { 3 int val; 4 struct ListNode *next; 5 ListNode(int x) : 6 val(x), next(NULL) { 7 } 8 };*/ 9 class Solution { 10 public: 11 ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) { 12 if(pListHead==NULL||k==0) 13 return NULL; 14 ListNode* pAhead=pListHead; 15 ListNode* pBehind; 16 for(unsigned int i=0;i<k-1;i++) 17 { 18 if(pAhead->next!=NULL) 19 pAhead=pAhead->next; 20 else 21 return NULL; 22 } 23 24 pBehind=pListHead; 25 while(pAhead->next!=NULL) 26 { 27 pAhead=pAhead->next; 28 pBehind=pBehind->next; 29 } 30 return pBehind; 31 } 32 };
【举一反三】
1、求链表的中间节点:如果链表中节点总数为奇数,返回中间节点;如果节点总数为偶数,返回中间节点中的任意一个。
解题思路:定义两个指针,同时从链表头结点出发,慢指针一次走一下,快指针一次走两步,这样当快指针到达链表末尾的时候,慢指针刚好到达链表的中间位置。
代码实现:
1 /* 2 struct ListNode { 3 int val; 4 struct ListNode *next; 5 ListNode(int x) : 6 val(x), next(NULL) { 7 } 8 };*/ 9 class Solution { 10 public: 11 ListNode* FindMiddle(ListNode* pListHead) { 12 if( pListHead==NULL)//头结点为空,直接返回 13 return NULL; 14 if(pListHead->next=NULL||pListHead->next->next==NULL)//只有一个或者两个节点,直接返回头结点 15 return pListHead; 16 ListNode* pAhead=pListHead; 17 ListNode* pBehind=pListHead; 18 while(pAhead&&pAhead->next) 19 { 20 pAhead=pAhead->next->next; 21 pBehind= pBehind->next; 22 } 23 return pBehind; 24 } 25 };
思考:同理可以引申出找到链表1/n位置处的节点,只要让快指针每次走n步,慢指针每次走1步,保持n:1的比例即可。
2、有序链表寻找中位数
解题思路:
代码实现:
/* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) { } };*/ class Solution { public: int * FindMiddle(ListNode* pListHead) { if( pListHead==NULL)//头结点为空,直接返回 return false; ListNode* pAhead,pBehind; pAhead=pBehind=pListHead; while (pAhead&&pBehind) { if (pAhead->next==NULL) return pBehind ->val; else if (pAhead->next!= NULL && pAhead->next->next== NULL) return (pBehind ->val + pBehind ->next->val)/2; else { pAhead= pAhead->next->next; pBehind = pBehind ->next; } } } };
3、判断一个单链表是否形成了环形结构。
解题思路:定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步。如果走得快的指针追上了走得慢的指针,那么链表就是环形结构;而如果快指针走到了链表的末位都没有追上走得慢的指针,那么链表就不是环形结构。
代码实现:
1 /* 2 struct ListNode { 3 int val; 4 struct ListNode *next; 5 ListNode(int x) : 6 val(x), next(NULL) { 7 } 8 };*/ 9 class Solution { 10 public: 11 bool isExitsLoop(ListNode* pListHead) { 12 if( pListHead==NULL)//头结点为空,直接返回 13 return false; 14 ListNode* pAhead,pBehind; 15 pAhead=pBehind=pListHead; 16 while(pAhead&&pAhead->next) 17 { 18 pAhead=pAhead->next->next; 19 pBehind=pBehind->netx; 20 if(pAhead==pBehind) 21 return true; 22 } 23 return false; 24 } 25 };
进一步思考:如果存在环,如何判断环的入口位置?
代码实现:
1 /* 2 struct ListNode { 3 int val; 4 struct ListNode *next; 5 ListNode(int x) : 6 val(x), next(NULL) { 7 } 8 };*/ 9 class Solution { 10 public: 11 ListNode findLoopPoint(ListNode* pListHead) { 12 if( pListHead==NULL)//头结点为空,直接返回 13 return false; 14 ListNode* pAhead,pBehind; 15 pAhead=pBehind=pListHead; 16 while(pAhead&&pAhead->next) 17 { 18 pAhead=pAhead->next->next; 19 pBehind=pBehind->netx; 20 if(pAhead==pBehind) 21 break; 22 } 23 if(pAhead==NULL||pAhead->next==NULL) 24 return NULL; 25 ListNode* pList=pListHead; 26 while(pListHead!=pBehind) 27 { 28 pList=pList->next; 29 pBehind=pBehind>next; 30 } 31 return pList;//或者 return pBehind; 32 } 33 };