《从头再来》剑指offer.22 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
最简单的方法:前后双指针。前指针先向前走k步,然后前指针不为空的时候,前后指针一起移动,直到前指针为nullptr时,返回后指针,后指针的位置就是倒数第k个节点。
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* getKthFromEnd(ListNode* head, int k) { ListNode *front = head; ListNode *rear = head; //前指针向前移动k步 for(int i = 0; i < k; ++i){ front = front->next; }//此处for循环可以优化,使用while(k--),移动 //前后指针一起移动 while(front != nullptr){ front = front->next; rear = rear->next; } return rear; } };
第二种方法:借助栈来解决。当头节点不为空的时候,将所有结点压入栈,然后弹栈k-1个元素,此时栈顶元素就是我们想要的结点,直接返回栈顶元素即可。
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* getKthFromEnd(ListNode* head, int k) { //栈,先压栈,再弹k-1个栈,然后返回栈顶元素(链表的东西,多画图) stack<ListNode*> stk; while(head != nullptr){ stk.push(head); head = head->next; } while(--k) stk.pop(); return stk.top(); } };
这里又提到了双指针,那么就将双指针和链表的相爱相杀说一下吧。
链表是一个很难得到长度和具体位置的东西,相对于数组的顺序存储来说,获取链表的长度和某个节点的具体位置时很难办到的。但是面试中又一定会问到关于长度和位置的问题,那我们就借用双指针来解决一下吧。
常见问题1、链表中的倒数第k个节点。
解法就是上面所提到的前后双指针,先前指针移动k位,然后前后指针一起移动,直到前指针指向nullptr,返回后指针的位置即为倒数第k个节点。
常见问题2、如何取得链表的中点?
这个问题要分两种情况,需要看节点的个数是奇数还是偶数。但不管奇数还是偶数个节点个数,都是使用快慢指针。快慢指针的意思是说,如果没有到链表的尾部,快指针每次都移动两步,慢指针每次移动一步直到快指针不能移动两步的时候,慢指针指向的位置就是链表的中点(奇数个节点)/中点靠前的节点(偶数个节点)。但是如果是偶数个节点如何取到靠后的节点呢?只需要同时限制快指针的next也不为nullptr即可。
常见问题3、如何判断链表是否有环?
同样的快慢双指针,每次快指针移动两步,慢指针移动一步,如果快慢指针相遇了(fast == slow),则证明有环。
常见问题4、问题3的基础上,如何判断环的长度?
继续移动双指针,直到第二次相遇,两次相遇之间移动的次数就是环的长度。
常见问题5、问题3的基础上,如何找到环的开头?
快慢指针相遇以后,将快指针移动到链表的开头,然后和慢指针一样一次移动一步,相遇的地方就是环的开头。
《从头再来》

浙公网安备 33010602011771号