链表10-12寻找公共节点、链表相加、链表排序

10 寻找两个链表的第一个公共节点

在这里插入图片描述
第一种思路是使用set,把第一个链表全部放入集合之后,在第二个链表遍历的过程中一一判断该节点是否在集合中,这样时间复杂度O(M+N),空间复杂度O(N),不符合要求。

要O(1)的空间复杂度一般就是用双指针,一开始没有想清楚,看了题解恍然大悟,让两个指针分别指向两个链表头,本链表遍历完之后回到另一个链表的头,这样两个指针遍历的长度是一样的,让它们用一样的步长,就可以判断是否有相同节点。时间复杂度O(m+n),空间复杂度O(1).

看到题解还有另一种做法是先找到两个链表各自的长度,让长的那个先走,让两个指针剩余要走的长度一样的时候再同时遍历,本质上还是和上面的思路一样。

import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode p1 = pHead1;
        ListNode p2 = pHead2;
        while(p1!=null && p2!=null){
            if(p1 == p2) return p1;
            p1 = p1.next;
            p2 = p2.next;
            if(p1==null) p1 = pHead2; //表1遍历完回到表2头
            else if(p2 == null) p2 = pHead1;//表2遍历完回到表1头
        }
        return null;
    }
}

在这里插入图片描述
需要注意的是!!不能用p1.next=pHead2!!!,因为后面相同的节点会直接形成一个环,永远都不会再出现null!!!
在这里插入图片描述

11. 链表相加

在这里插入图片描述
一开始没看数据范围,就直接把两个链表遍历算出整数相加再取出各个位的数值,提交后全是错的,因为超出了数据范围,改成了Long也不行,因为位数mn都有10的六次方,显而易见用多长的整形都是不够用的,而且这种做法写起来也并没有简单,必须要一位一位求。
思路很直接,就是先反转一遍链表,从两个尾部开始相加,每次记录最后一位和进位,形成链表最后再反转。时间复杂度O(M和n中较大的那个),空间复杂度O(1)
很想夸一下自己这些都是写完一遍就是正确答案,不需要任何修改。

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head1 ListNode类 
     * @param head2 ListNode类 
     * @return ListNode类
     */
    public ListNode addInList (ListNode head1, ListNode head2) {
        // write code here     
        ListNode pre = null;
        ListNode cur = head1;
        while(cur!=null){
            ListNode tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        ListNode p1 = pre;//反转后的头
        
        pre = null;
        cur = head2;
        while(cur!=null){
            ListNode tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        ListNode p2 = pre;
        ListNode res = new ListNode(-1);
        cur = res;
        int n = 0;
        int m = 0;
        while(p1!=null||p2!=null){
            if(p1==null){
                m = (p2.val+n)%10;
                n = (p2.val+n)/10;          
            }
            else if(p2==null){
                m = (p1.val+n)%10;
                n = (p1.val+n)/10;                 
            }
            else {
                m = (p1.val+p2.val+n)%10;
                n = (p1.val+p2.val+n)/10;//进位              
            }
            ListNode node = new ListNode(m);
            cur.next = node;
            cur = cur.next;
            if(p1!=null) p1 = p1.next;
            if(p2!=null) p2 = p2.next;           
        }
        //开始反转
        pre = null;
        cur = res.next;
        while(cur!=null){
            ListNode tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
}

在这里插入图片描述
看了一下别人的代码,精简了一下

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head1 ListNode类 
     * @param head2 ListNode类 
     * @return ListNode类
     */
    public ListNode addInList (ListNode head1, ListNode head2) {
        // write code here
        ListNode p1 = reverse(head1);
        ListNode p2 = reverse(head2);
        ListNode res = new ListNode(-1);
        ListNode cur = res;        

        int jin = 0;
        int ge = 0;


        while(p1!=null||p2!=null){
            int val1 = p1==null? 0:p1.val;
            int val2 = p2==null? 0:p2.val;
            ge = (val1+val2+jin)%10;
            jin = (val1+val2+jin)/10;
            cur.next = new ListNode(ge);
            cur = cur.next;
            if(p1!=null)p1 = p1.next;
            if(p2!=null)p2 = p2.next;
        }
        if(jin!=0) cur.next = new ListNode(jin); //最后一个进位
       
        return reverse(res.next);

    }
    public ListNode reverse(ListNode head){
        ListNode cur = head, pre = null;
        while(cur!=null){
            ListNode tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }


}

在这里插入图片描述
节省了20行的代码量,逻辑也更清晰。
主要是

            int val1 =  p1==null? 0: p1.val;
            int val2 =  p2==null? 0: p2.val; 

这两行。

11. 链表排序

在这里插入图片描述
想法非常直接和简单,把所有的值存到数组中,排序之后再建表,排序时间复杂度刚好是O(Nlogn),空间复杂度O(N)

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 the head node
     * @return ListNode类
     */
    public ListNode sortInList (ListNode head) {
        // write code here
        ArrayList<Integer> array = new ArrayList<Integer>();
        while(head!=null){
            array.add(head.val);
            head = head.next;
        }
        Collections.sort(array);
        //list.sort(((a,b)->a-b));也一样
        ListNode res = new ListNode(-1);
        ListNode cur = res;
        for(int i = 0; i< array.size(); i++){
            cur.next = new ListNode(array.get(i));
            cur = cur.next;
        }
        return res.next;
    }
}

在这里插入图片描述
第二种就是归并排序,时间复杂度O(nlogn),空间复杂度O(logn),空间复杂度主要取决于递归调用的栈空间

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 the head node
     * @return ListNode类
     */
    public ListNode sortInList (ListNode head) {
        // write code here
        if(head==null|| head.next==null) return head;
        ListNode slow = new ListNode(-1);
        slow.next = head;
        ListNode fast = head;
        while(slow!=null && fast!=null && fast.next!=null){
            fast = fast.next;
            slow = slow.next;
        }
        //用快慢指针找中点,快指针到结尾时,慢指针刚好到中间
        ListNode mid = slow.next;
        slow.next = null;//断开,分成两个部分;
        return merge(sortInList(head),sortInList(mid)); //归并      
    }
    public ListNode merge(ListNode p1, ListNode p2){
        ListNode res = new ListNode(-1);
        ListNode cur = res;
        while(p1!=null&& p2!=null){
            if(p1.val<p2.val){
                cur.next = p1;
                cur = cur.next;
                p1 = p1.next;
            }
            else{
                cur.next = p2;
                cur = cur.next;
                p2 = p2.next;
            }           
        }
        if(p1 == null) cur.next = p2;
        else if(p2 == null) cur.next = p1;
        return res.next;
        
    }
}

看了题解才知道还有一种自底向上的归并排序,不需要递归,可以节省空间,只需要O(1)的空间复杂度。
使用自底向上的方法实现归并排序,则可以达到 O(1)O(1) 的空间复杂度。

首先求得链表的长度 \textit{length}length,然后将链表拆分成子链表进行合并。

具体做法如下。

用subLength 表示每次需要排序的子链表的长度,初始时subLength=1。

每次将链表拆分成若干个长度为subLength 的子链表(最后一个子链表的长度可以小于 subLength),按照每两个子链表一组进行合并,合并后即可得到若干个长度为subLength×2 的有序子链表(最后一个子链表的长度可以小于subLength×2)。

将subLength 的值加倍,重复第 2 步,对更长的有序子链表进行合并操作,直到有序子链表的长度大于或等于length,整个链表排序完毕。

如何保证每次合并之后得到的子链表都是有序的呢?可以通过数学归纳法证明。

初始时subLength=1,每个长度为 11 的子链表都是有序的。

如果每个长度为subLength 的子链表已经有序,合并两个长度为 subLength 的有序子链表,得到长度为subLength×2 的子链表,一定也是有序的。

当最后一个子链表的长度小于subLength 时,该子链表也是有序的,合并两个有序子链表之后得到的子链表一定也是有序的。

因此可以保证最后得到的链表是有序的。

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 the head node
     * @return ListNode类
     */
    public ListNode sortInList (ListNode head) {
        // write code here
        if(head == null || head.next == null) return head;
        ListNode cur = head;
        ListNode tmp = new ListNode(-1);
        tmp.next = head;
        int length = 0;
        while(cur!=null){
            cur = cur.next;
            length++;
        }

        ListNode readysubhead = tmp;//排好序的子段头
        ListNode res = readysubhead;
        for(int sublength = 1; sublength < length; sublength =sublength * 2){
            cur = tmp.next;
            readysubhead = tmp;
            while(cur!=null){//遍历所有的子段
                ListNode l1 = cur;
                for(int i = 1; i< sublength; i++)
                {
                    cur= cur==null?cur:cur.next;//否则数组越界
                }
                ListNode l2 = cur==null?null:cur.next;
                if(cur!=null) cur.next = null;//分割出l1子段
                cur = l2;
                for(int i = 1; i< sublength; i++)
                {
                    cur= cur==null?cur:cur.next;
                }
                ListNode subhead = cur==null?null:cur.next;
                if(cur!=null) cur.next = null;//分割出l2子段
                readysubhead.next = merge(l1,l2);

                while(readysubhead.next!=null){
                    readysubhead = readysubhead.next;
                   // System.out.print(readysubhead.val);
                    
                } 
                readysubhead.next = subhead;
                cur = subhead;               
            }
        }
        return res.next;

    }
    public ListNode merge(ListNode l1, ListNode l2){
        ListNode res = new ListNode(-1);
        ListNode cur = res;
        while(l1!=null && l2!=null){
            if(l1.val<l2.val){
                cur.next = l1;
                l1 = l1.next;
            }
            else{
                cur.next = l2;
                l2 = l2.next;
            }
            cur = cur.next;
        }
        if(l1==null) cur.next = l2;
        else if(l2==null) cur.next = l1;
        
        return res.next;
    }
}

debug了非常久,主要是readysubhead忘记更新和cur数组越界的判断问题,需要再练习一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值