面试-面试常见的链表算法捷径(二)

上一篇在最后给大家留了拓展题,不知道大家有没有思考完成,其实之前说有巨坑是吓大家了,实际上也没什么,好了,我们继续来看上一篇中的拓展题。

面试题:给定单链表的头结点,删除单链表的倒数第k个结点。

这个题和前面的文章中增加了一个操作,除了找出来这个结点,我们还要删除它。删除一个结点,想必大家必定也知道:要想操作(添加、删除)单链表的某个结点,那我们还得知道这个节点的前一个结点。所以我们要删除倒数第k个结点,就必须要找到倒数第k+1个结点。然后把倒数第k+1个元素的next变量p.next指向p.next.next;

我们找到倒数第k个结点的时候,先让fast走了k-1步,然后再让slow变量和fast变量同步走,这样他们之前一直保持k-1的距离,所以当fast到链表尾节点的时候,slow刚刚指向的是倒数第k个结点。

本题由于我们要知道倒数第k+1个结点,所以得让fast先走k步,待fast指向链表的尾结点的时候,slow正好指向倒数第k+1个结点。

我们简单思考一下临界值:

  1. 当k=1的时候,删除的值是尾结点。我们让fast先走1步,当fast.next为尾结点的时候,倒数第k+1个结点正好是我们的倒数第二个结点。我们删除slow.next,并让slow.next指向slow.next.next=null,满足条件。
  2. 当k>len的时候,我们要找的倒数第k个元素不存在,直接出错。
  3. 当1<k<len的时候,k最大为len-1的时候,fast移动len-1步,直接到达尾结点,此时,slow指向头结点。删除倒数第k个元素,即删除正数第2个结点即可。
  4. 当 k=len 的时候比较特殊,当fast移动len步的时候,已经指向了fast.next = null ,此时我们其实要删除的是头结点,直接返回head.next即可。

所以我们自然得到了这样的代码。

    private static LinkNode delTheSpecifiedReverse(LinkNode head, int k) {
        LinkNode slow = head;
        LinkNode fast = head;
        if (fast == null) {
            throw new RuntimeException("your linkNode is null");
        }
        //先让fast先走k步
        for (int i = 0; i < k; i++) {
            if (fast == null) {
                //说明输入的k已经超过了链表的长度,直接报错
                throw new RuntimeException("the value k is too large");
            }
            fast = fast.next;
        }
        //fast == null ,说明已经到了尾结点后边的空区域,说明要删除的是头结点
        if (fast == null) {
            return head.next;
        }
        while (fast.next != null) {
            slow = slow.next;
            fast = fast.next;
        }
        slow.next = slow.next.next;
        return head;
    }

    public static void main(String[] args) {
        LinkNode head = new LinkNode(1);
        head.next = new LinkNode(2);
        head.next.next = new LinkNode(3);
        head.next.next.next = new LinkNode(4);
        head.next.next.next.next = new LinkNode(5);
//        System.out.println("getTheMid2:"+getTheMid2(head));
//        head.next.next.next.next.next = head;
//        System.out.println("isRingLink:"+isRingLink(head));
//        System.out.println("getSpecifiedNodeReverse:" + getSpecifiedNodeReverse(head, 3));
//        System.out.println("getSpecifiedNodeReverse:" + getSpecifiedNodeReverse(null, 1));
//        System.out.println("未删除前链表为:");
        LinkNode old=head;
        while (old != null) {
            System.out.print(old.data + "->");
            old = old.next;
        }
        System.out.println("");
        LinkNode node = delTheSpecifiedReverse(head, 3);
        System.out.println("删除后链表为:");
        while (node != null) {
            System.out.print(node.data + "->");
            node = node.next;
        }
    }

输出结果为:

1->2->3->4->5->
删除后链表为:
1->2->4->5->

OK,我们现在已经解决了上篇文章中留下的拓展题,现在我们来看看我们的链表都还有怎样的考法。

面试题:定义一个单链表,输入一个链表的头结点,反转该链表,并输出反转后链表的头结点。为了方便,我们链表的data采用整型

这是一个反转链表的经典题,我们来屡一下思路:一个结点包含下一个结点的引用,反转的意思是就是要把原来指向下一结点的引用指向上一个结点。我们可以分为下面的步骤:

  1. 找到当前要反转的结点的下一个结点,并用变量保存,因为下一次要反转的是它,如果我们不保存的话,一定会因为前面已经反转,导致无法通过遍历得到这个结点;
  2. 然后让当前结点的next引用指向上一个结点,上一个结点初试null,因为头结点的反转后变成尾结点;
  3. 当前要反转的结点变成下一个要比较元素的上一个结点,用变量保存;
  4. 当前要比较的结点赋值为之前保存的未反转前的下一个结点;
  5. 当前反转的结点为null的时候,保存的上一个结点即为反转后的链表头结点。

用代码实现就是:

private static LinkNode reverseLink(LinkNode head) {
        //上一个结点
        LinkNode nodePre = null;
        LinkNode next = null;
        LinkNode node = head;
        while (node!=null){
            //先用next保存下一个要反转的结点,不然会导致链表断裂.
            next=node.next;
            //在把现在结点的next引用指向上一个结点
            node.next=nodePre;
            //把当前结点赋值给nodePre变量,以便于下一次赋值
            nodePre=node;
            //向后遍历
            node=next;
        }
        return nodePre;
    }

    public static void main(String[] args) {
        LinkNode head = new LinkNode(1);
        head.next = new LinkNode(2);
        head.next.next = new LinkNode(3);
        head.next.next.next = new LinkNode(4);
        head.next.next.next.next = new LinkNode(5);
//        System.out.println("getTheMid2:"+getTheMid2(head));
//        head.next.next.next.next.next = head;
//        System.out.println("isRingLink:"+isRingLink(head));
//        System.out.println("getSpecifiedNodeReverse:" + getSpecifiedNodeReverse(head, 3));
//        System.out.println("getSpecifiedNodeReverse:" + getSpecifiedNodeReverse(null, 1));
//        System.out.println("未删除前链表为:");

//        LinkNode old = head;
//        while (old != null) {
//            System.out.print(old.data + "->");
//            old = old.next;
//        }
//        System.out.println("");
//        LinkNode node = delTheSpecifiedReverse(head, 3);
//        System.out.println("删除后链表为:");
//        while (node != null) {
//            System.out.print(node.data + "->");
//            node = node.next;
//        }

        LinkNode node=reverseLink(head);
        while (node!=null){
            System.out.print(node.data+"->");
            node=node.next;
        }
    }

输出结果为:5->4->3->2->1->

链表可以考的可是真多,相信不是小伙伴都和我一样,云里雾里了,今天就先说到这里了。

本来来自nanchen的分享,喜欢的去关注一下公众号,文章地址

 

    原文作者:L竹轩沐雨
    原文地址: https://blog.csdn.net/lxm20819/article/details/82755474
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞