zoukankan      html  css  js  c++  java
  • LeetCode 链表:21合并两个有序链表 19删除链表的倒数第N个节点

     

    21合并两个有序链表

     思路:

    这个题目搞出来了,但是由于没有用哨兵这个技巧,很不简洁。

    思路很简单,新的链表有一个头head,有一个尾end.遍历链表,while(p1!=null&p2!=null),比较出p1和p2中最小的,把这个最小的添加到尾部end的后面作为新的尾部,然后p1或者p2指针后移一位。等到遍历完,需要看p1或者p2是否不为null,如果不为null,应该将其添加到尾部

    问题来了,新链表的头部在遍历之前是不确定的(为p1和p2中较小的一个),而且这同时也是第一次遍历时新链表的尾部,从而尾部也无法确定。导致需要把第一次循环单拿出来做确定初始的头和尾

    第二个问题:第一次遍历还不能直接比较p1和p2的大小,因为p1可能为空。。。所以还要加一个判断。

    先看一下一开始写的渣渣代码

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) { val = x; }
     * }
     */
    class Solution {
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            ListNode res,end,p1=l1,p2=l2;
            if(l1==null)
            {
                return l2;
            }
            else if(l2==null)
            {
                return l1;
            }
            if(l1.val<=l2.val)
            {
                res=l1;
                end=l1;
                p1=p1.next;
            }
            else
            {
            res=l2;
            end=l2;
            p2=p2.next;
            }
    
            while(p1!=null&p2!=null)
            {
                if(p1.val<=p2.val)
                {
                    end.next=p1;
                    end=p1;
                    p1=p1.next;
                }
                else
                {
                    end.next=p2;
                    end=p2;
                    p2=p2.next;
                }
            }
            if(p1==null)
            {
                end.next=p2;
            }
            else if(p2==null)
            {
                end.next=p1;
            }
    
            return res;
        }
    }
    一开始代码

    哨兵的想法很好

    先指定一个prehead,当然它也是新链表的初始尾。这样就可以不用写那些多余的代码了,第一次循环和其他循环都一样,只用end.next=p1和p2中取小就行了。最终结果的头就是prehead.next

    更加巧妙的是,这样还能避免p1或者p2为null的问题。

    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
              ListNode preHead=new ListNode(0);
            ListNode end=preHead;
            while(l1!=null&l2!=null)
            {
                if(l1.val<=l2.val)
                {
                    end.next=l1;
                    end=end.next;
                    l1=l1.next;
                }
                else
                {
                    end.next=l2;
                    end=end.next;
                    l2=l2.next;
                }
            }
            end.next=(l1==null)?l2:l1;
            return preHead.next;
        }

    此题还有递归方法,日后再说。

    19删除链表的倒数第N个节点

     思路:这道题做出来了,很快就有了思路,写出来后碰了个null.next的问题,之后也解决了

    要找倒数第N节点,必然要找最后一个节点。但是由于单向链表是“开弓没有回头箭”,怎么在找到末尾节点时找到其前第n个节点就是问题。

    之前碰到过的大量双指针问题迅速给了我思路,让第一个指针先走n步,再两个节点一起往前走就行了。多么简洁的解题思路!

    理清一下两个指针的关系:先遍历n次,这时p1 p2距离为n

    p1为null时,p2为倒数第n个节点,但是我们要删除倒数n节点显然要找的是倒数第n+1个节点,p(倒数n+1).next=p(倒数n+1).next.next。

    p1为最后一个节点时,p2为倒数第n+1个节点,显然这样更好。

    因此,这里的第二次循环的条件设置为while(p1.next!=null),最终指针停留在最后一个节点。

    也想过增加距离然后用p1=null判定,但这会让第一次循环时第一个节点更容易越界。

    下一个问题来了,假如长度为length,n也为length。第一次循环后p1指向null,第二次循环一开始就会是null.next错误,因此还应在第一次循环后再加一个判断,判断是否p1=null,这等价于n=lenth,只用删除第一个节点就行。

    我最终代码如下

    class Solution {
        public ListNode removeNthFromEnd(ListNode head, int n) {
             ListNode p1=head,p2=head;
            for(int i=0;i<n;i++)
            {
                p1=p1.next;
            }
            if(p1==null)
            {return head.next;}
    
            while(p1.next!=null)
            {
                p1=p1.next;
                p2=p2.next;
            }
            p2.next=p2.next.next;
            return head;
        }
    }

    LeetCode官方解法:

    诶,碰到这种null.next,应该要想到dummy这个技巧的!

    简而言之,我们只需要p1和p2拉开n的距离,但跑道太短,先走n步会撞墙,所以大家先都倒退一步再开始走就行了。

    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode first = dummy;
        ListNode second = dummy;
        // Advances first pointer so that the gap between first and second is n nodes apart
        for (int i = 1; i <= n + 1; i++) {
            first = first.next;
        }
        // Move first to the end, maintaining the gap
        while (first != null) {
            first = first.next;
            second = second.next;
        }
        second.next = second.next.next;
        return dummy.next;
    }
    
    作者:LeetCode
    链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/solution/shan-chu-lian-biao-de-dao-shu-di-nge-jie-dian-by-l/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    PS:看来LeetCode也会出提交的bug,明明都是O1的空间复杂度,只战胜了百分之5的空间复杂度可还行。剩下百分之95怕不是用的量子计算机(滑稽)。

  • 相关阅读:
    mybatis源码(三)SqlSession执行mapper的过程上篇
    Mybatis源码(二)mybatis核心组件介绍
    Mybatis源码(一) 之SqlSession的创建
    设计模式的分类及其六大设计原则
    设计模式之桥接模式
    设计模式之解释器模式
    设计模式之享元模式
    hdu3652
    Codeforces Round #703 (div2)
    Educational Codeforces Round 104
  • 原文地址:https://www.cnblogs.com/take-it-easy/p/12721113.html
Copyright © 2011-2022 走看看