【数据结构和算法】

1,双指针求解

这题要求链表的倒数第k个节点,最简单的方式就是使用两个指针,第一个指针先移动k步,然后第二个指针再从头开始,这个时候这两个指针同时移动,当第一个指针到链表的末尾的时候,返回第二个指针即可。注意,如果第一个指针还没走k步的时候链表就为空了,我们直接返回null即可。
剑指offer JZ14:链表中倒数第k个结点(双指针,栈,递归3种解决方式) - 图1

  1. public ListNode FindKthToTail(ListNode pHead, int k) {
  2. if (pHead == null)
  3. return pHead;
  4. ListNode first = pHead;
  5. ListNode second = pHead;
  6. //第一个指针先走k步
  7. while (k-- > 0) {
  8. if (first == null)
  9. return null;
  10. first = first.next;
  11. }
  12. //然后两个指针在同时前进
  13. while (first != null) {
  14. first = first.next;
  15. second = second.next;
  16. }
  17. return second;
  18. }

2,使用栈解决

这题要求的是返回后面的k个节点,我们只要把原链表的结点全部压栈,然后再把栈中最上面的k个节点出栈,出栈的结点重新串成一个新的链表即可,原理也比较简单,直接看下代码。

  1. public ListNode FindKthToTail(ListNode pHead, int k) {
  2. Stack<ListNode> stack = new Stack<>();
  3. //链表节点压栈
  4. int count = 0;
  5. while (pHead != null) {
  6. stack.push(pHead);
  7. pHead = pHead.next;
  8. count++;
  9. }
  10. if (count < k || k == 0)
  11. return null;
  12. //在出栈串成新的链表
  13. ListNode firstNode = stack.pop();
  14. while (--k > 0) {
  15. ListNode temp = stack.pop();
  16. temp.next = firstNode;
  17. firstNode = temp;
  18. }
  19. return firstNode;
  20. }

3,递归求解之前讲过链表的逆序打印410,剑指 Offer-从尾到头打印链表,其中有这样一段代码

  1. public void reversePrint(ListNode head) {
  2. if (head == null)
  3. return;
  4. reversePrint(head.next);
  5. System.out.println(head.val);
  6. }

这段代码其实很简单,我们要理解他就要弄懂递归的原理,递归分为两个过程,,看一下下面的图就知道了,先往下传递,当遇到终止条件的时候开始往回走。
剑指offer JZ14:链表中倒数第k个结点(双指针,栈,递归3种解决方式) - 图2
前面也刚讲过递归的原理426,什么是递归,通过这篇文章,让你彻底搞懂递归,这题如果使用递归的话,我们先来看一下递归的模板

  1. public void reversePrint(ListNode head) {
  2. if (head == null)
  3. return;
  4. reversePrint(head.next);
  5. System.out.println(head.val);
  6. }

终止条件很明显就是当节点head为空的时候,就没法递归了,这里主要看的是逻辑处理部分,当递归往下传递到最底端的时候,就会触底反弹往回走,在往回走的过程中记录下走过的节点,当达到k的时候,说明到达的那个节点就是倒数第k个节点,直接返回即可,如果没有达到k,就返回空,搞懂了上面的过程,代码就很容易写了

  1. //全局变量,记录递归往回走的时候访问的结点数量
  2. int size;
  3. public ListNode FindKthToTail(ListNode pHead, int k) {
  4. //边界条件判断
  5. if (pHead == null)
  6. return pHead;
  7. ListNode node = FindKthToTail(pHead.next, k);
  8. ++size;
  9. //从后面数结点数小于k,返回空
  10. if (size < k) {
  11. return null;
  12. } else if (size == k) {
  13. //从后面数访问结点等于k,直接返回传递的结点k即可
  14. return pHead;
  15. } else {
  16. //从后面数访问的结点大于k,说明我们已经找到了,
  17. //直接返回node即可
  18. return node;
  19. }
  20. }

上面代码在仔细一看,当size小于k的时候node节点就是空,所以我们可以把size大于k和小于k合并为一个,这样代码会更简洁一些

  1. int size;
  2. public ListNode FindKthToTail(ListNode pHead, int k) {
  3. if (pHead == null)
  4. return pHead;
  5. ListNode node = FindKthToTail(pHead.next, k);
  6. if (++size == k)
  7. return pHead;
  8. return node;
  9. }