- 问题描述:
输入一个链表,输出该链表中倒数第k个结点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾结点是倒数第一个结点。例如一个链表有6个结点,从头结点开始它们依次是1、2、3、4、5、6。这个链表的倒数第三个结点是值为4的结点。结点定义如下:
struct ListNode{ int m_nValue; ListNode* m_pNext; }
-
分析:
要得到倒数第k个结点,也就是正数第n-k+1(其中n为结点总数)个结点,所以又有了最简单粗暴的方法:先遍历一次链表,每遍历一个结点计数器加一,最后得到n,然后第二次遍历链表,找到第n-k+1个结点,问题解决。
当然,面试官会希望我们只遍历一次链表就得到结果,那么我们就需要定义两个指针了。我们希望遍历结束的时候,一个指针指向末尾结点,而另一个指针指向倒数第k个结点。所以,我们用第一个指针从头遍历链表,当其指向第k个结点的时候,令第二个指针指向第一个结点,然后两个指针一起向前遍历,知道第一个指针到达链表末尾,此时第二个指针所指的结点即倒数第k个结点。代码如下:ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) { if(pListHead == NULL || k == 0) return NULL; ListNode *pAhead = pListHead; ListNode *pBehind = NULL; for(int i = 0; i < k-1; ++i) { if(pAhead->m_pNext != NULL) pAhead = pAhead->m_pNext; else return NULL; } pBehind = pListHead; while(pAhead->m_pNext != NULL) { pAhead = pAhead->m_pNext; pBehind = pBehind->m_pNext; } return pBehind; }
值得注意的是,我们不仅要写出代码,还要考虑代码的鲁棒性。以此题为例,我们需要考虑一下三种情况:
- 输入的链表头指针pListHead为空指针
- 输入的参数k为0
- 输入的以pListHead为头结点的链表中的结点总数少于k
-
相关题目:
1)求链表结点的中间结点。如果链表结点的总数为奇数,则返回中间结点;如果结点总数是偶数,则返回中间两个结点中的任意一个。
思路:依然使用两个指针,这两个指针同时从头结点开始,一个一次向后移两步,一个一次向后移一步,则当走的快的指针到达链表末尾时,走的慢的指针刚好在链表中间。
2)判断一个单链表是否形成了环形结构。
解题思路:对于一个链表的问题,用一个指针遍历不能解决问题时,可以尝试使用两个指针来遍历链表。并让一个指针遍历的速度快一些,或者让它先在链表上走若干步。