【链表】剑指 Offer 22. 链表中倒数第k个节点

题目描述

链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

类似的题目还有返回倒数第k个节点的值

解题思路

两趟扫描

  1. 分两趟扫描链表
  2. 第一趟用于获取链表长度size,然后算出第二趟应该扫描的节点个数size-k。
  3. 第二趟从头开始遍历size-k个节点。得到的即为倒数第k个节点
class Solution {
    
    
    public ListNode getKthFromEnd(ListNode head, int k) {
    
    
        ListNode cur = head;
        int count = 0;
        // 获取链表节点个数
        while (cur != null) {
    
    
            count++;
            cur = cur.next;
        }
        // 遍历得到倒数第k个节点
        for (int i = 0; i < count - k ; i++) {
    
    
            head = head.next;
        }
        return head;
    }

快慢指针 一趟扫描

  1. 使用两个指针向后扫描,
  2. 一个快指针多走k步,一个慢指针指向头节点
  3. 两个节点同时向后走,当快指针到头时,慢指针指向的即为倒数第k个节点。
class Solution {
    
    
    public ListNode getKthFromEnd(ListNode head, int k) {
    
    
        // 快慢指针先都指向head
        ListNode fast = head;
        ListNode slow = head;
        // 快指针多走k步
        while (k-- != 0) {
    
    
           fast = fast.next;
        }
        // 两个指针同步向后 直到快指针为空 此时慢指针即为倒数第k个节点
        while (fast != null) {
    
    
            fast = fast.next;
            slow = slow.next;
        }
        return slow;

  1. 先遍历链表,把所有节点入栈,此时出栈顺序即为链表倒序
  2. 出栈k-1个节点,此时栈顶即为倒数第k个节点
class Solution {
    
    
    public ListNode getKthFromEnd(ListNode head, int k) {
    
    
 		Stack<ListNode> stack = new Stack<>();
 		// 所有节点都入栈
        while (head != null) {
    
    
            stack.push(head);
            head = head.next;
        }
        // 出栈k-1个节点,此时栈顶即为倒数第k个节点
        k--;
        while (k-- != 0) {
    
    
            stack.pop();
        }
                return stack.peek();

    }
}

递归

弄懂递归的原理,递归分为两个过程,递和归,看一下下面的图就知道了,先往下传递,当遇到终止条件的时候开始往回走。
在这里插入图片描述
这题如果使用递归的话,我们先来看一下递归的模板

public ListNode getKthFromEnd(ListNode head, int k) {
    
    
    //终止条件
    if (head == null)
        return head;
    //递归调用
    ListNode node = getKthFromEnd(head.next, k);
    //逻辑处理
    ……
}

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

//全局变量,记录递归往回走的时候访问的结点数量
int size;

public ListNode getKthFromEnd(ListNode head, int k) {
    
    
    //边界条件判断
    if (head == null)
        return head;
    ListNode node = getKthFromEnd(head.next, k);
    ++size;
    //从后面数结点数小于k,返回空
    if (size < k) {
    
    
        return null;
    } else if (size == k) {
    
    
        //从后面数访问结点等于k,直接返回传递的结点k即可
        return head;
    } else {
    
    
        //从后面数访问的结点大于k,说明我们已经找到了,
        //直接返回node即可
        return node;
    }
}

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


int size;

public ListNode getKthFromEnd(ListNode head, int k) {
    
    
    if (head == null)
        return head;
    ListNode node = getKthFromEnd(head.next, k);
    if (++size == k)
        return head;
    return node;
}

猜你喜欢

转载自blog.csdn.net/qq_17677907/article/details/112801154