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怕不是用的量子计算机(滑稽)。

 

版权声明:本文为take-it-easy原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/take-it-easy/p/12721113.html