【数据结构和算法】双指针,栈,递归3种解决方式

链表中倒数第k个结点

http://www.nowcoder.com/questionTerminal/886370fe658f41b498d40fb34ae76ff9

1,双指针求解

这题要求链表的倒数第k个节点,最简单的方式就是使用两个指针,第一个指针先移动k步,然后第二个指针再从头开始,这个时候这两个指针同时移动,当第一个指针到链表的末尾的时候,返回第二个指针即可。注意,如果第一个指针还没走k步的时候链表就为空了,我们直接返回null即可。
image.png

    public ListNode FindKthToTail(ListNode pHead, int k) {
        if (pHead == null)
            return pHead;
        ListNode first = pHead;
        ListNode second = pHead;
        //第一个指针先走k步
        while (k-- > 0) {
            if (first == null)
                return null;
            first = first.next;
        }
        //然后两个指针在同时前进
        while (first != null) {
            first = first.next;
            second = second.next;
        }
        return second;
    }

2,使用栈解决

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

    public ListNode FindKthToTail(ListNode pHead, int k) {
        Stack<ListNode> stack = new Stack<>();
        //链表节点压栈
        int count = 0;
        while (pHead != null) {
            stack.push(pHead);
            pHead = pHead.next;
            count++;
        }
        if (count < k || k == 0)
            return null;
        //在出栈串成新的链表
        ListNode firstNode = stack.pop();
        while (--k > 0) {
            ListNode temp = stack.pop();
            temp.next = firstNode;
            firstNode = temp;
        }
        return firstNode;
    }

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

public void reversePrint(ListNode head) {
    if (head == null)
        return;
    reversePrint(head.next);
    System.out.println(head.val);
}

这段代码其实很简单,我们要理解他就要弄懂递归的原理,递归分为两个过程,,看一下下面的图就知道了,先往下传递,当遇到终止条件的时候开始往回走。
image.png
前面也刚讲过递归的原理426,什么是递归,通过这篇文章,让你彻底搞懂递归,这题如果使用递归的话,我们先来看一下递归的模板

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

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

    int size;

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

我把部分算法题整理成了PDF文档,截止目前总共有900多页,大家可以下载阅读
链接https://pan.baidu.com/s/1hjwK0ZeRxYGB8lIkbKuQgQ
提取码:6666

如果觉得有用就给个赞吧,还可以关注我的《牛客博客》查看更多的详细题解

数据结构和算法 文章被收录于专栏

专注于算法题的讲解,包含常见数据结构,排序,查找,动态规划,回溯算法,贪心算法,双指针,BFS和DFS等等

全部评论
这里用递归是有问题的,题目里面n的大小为0≤n≤10^5,使用递归会导致栈帧过多从而栈溢出StackOverflowError
4 回复 分享
发布于 2021-09-27 14:04
方法二出栈时不需要重新形成链表吧,原来的链表还在
1 回复 分享
发布于 2022-08-10 22:25
双指针是我第一个想到的,这里的递归用得也是妙。
点赞 回复 分享
发布于 2021-07-31 10:26
太牛了吧,老哥,Nice
点赞 回复 分享
发布于 2021-10-09 19:39
支持把Leetcode刷完
点赞 回复 分享
发布于 2021-10-09 19:39

相关推荐

点赞 评论 收藏
分享
粗心的雪碧不放弃:纯学历问题,我这几个月也是一直优化自己的简历,后来发现优化到我自己都觉得牛逼的时候,发现面试数量也没有提升,真就纯学历问题
点赞 评论 收藏
分享
评论
78
9
分享
牛客网
牛客企业服务