链表 148. 排序链表

题目内容

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

进阶:

你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

示例 1:
在这里插入图片描述

输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:
在这里插入图片描述

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:

输入:head = []
输出:[]

提示:

链表中节点的数目在范围 [0, 5 * 104] 内
-105 <= Node.val <= 105

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

java普通解答(效率低,但是简单)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        ArrayList<Integer> arr = new ArrayList<>();
        ListNode newhead=head;
        if(head==null){
            return head;
        }
        //循环遍历链表把值存进到arrayList中去
        while (newhead!=null){
            arr.add(newhead.val);
            newhead=newhead.next;
        }
        newhead=head;
        //调用集合工具类,实现对arrlist数组进行排序 默认升序
        Collections.sort(arr);
        //然后改变每个节点的值.
        for (int i = 0; i < arr.size(); i++) {
            newhead.val=arr.get(i);
            newhead=newhead.next;
        }

        return head;
    }
}

归并排序(自顶向下归并排序)

leetcode官方:
考虑到递归调用的栈空间,自顶向下归并排序的空间复杂度是 O(log⁡n)O(\log n)O(logn)。如果要达到 O(1)O(1)O(1) 的空间复杂度,则需要使用自底向上的实现方式。

自顶下下归并排序 (不难)

/**
* Definition for singly-linked list.
* public class ListNode {
*     int val;
*     ListNode next;
*     ListNode() {}
*     ListNode(int val) { this.val = val; }
*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
   public ListNode sortList(ListNode head) {
      //没有节点或者只有一个节点的时候直接返回(递归终止条件)
      if(head==null||head.next==null){
          return head;
      }
      //快慢指针找中间节点
      ListNode slow=head;
      ListNode fast=head;
      while(fast.next!=null&&fast.next.next!=null){
          slow=slow.next;
          fast=fast.next.next;
      }
     
     
       ListNode right=sortList(slow.next);  //让右边链表进行排序
       slow.next=null;  //为了让链表从中间断开,断开后才可以排序左边的..
       ListNode left=sortList(head);//让左边链表进行排序
      
       ListNode newHead=merge(left,right);  //合并两个链表,返回头节点
       return newHead;
   }
    public ListNode merge(ListNode left, ListNode right) {
       //申请个虚拟结点,放最后返回头结点用
       ListNode dummy=new ListNode(0);
       //改指针用来走
       ListNode cur=dummy;
       while(left!=null&&right!=null){ //遍历两个链表
           if(left.val<right.val){  //如果左边的值小,就让cur.next指向左边,同时cur向后移动;;
               cur.next=left;
               cur=left;
               left=left.next;  //left向后移动
           }else{   //同理右边大
               cur.next=right;
               cur=right;
               right=right.next;
           }
       }
       if(left!=null){  //如果左边链表没空,那么cur.next直接指向left
           cur.next=left;
       }else{  //右边不为空,或为空都走下面这句话  为空直接指向null,不需要单独区分
           cur.next=right;
       }
       return dummy.next;
   }
}

力扣官方题解(我加了注释,把merge变动了一点点)

class Solution {
    public ListNode sortList(ListNode head) {
        if (head == null||head.next==null) {
            return head;
        }
        int length = 0;  //求链表的长度
        ListNode node = head;
        while (node != null) {
            length++;
            node = node.next;
        }
        ListNode dummyHead = new ListNode(0, head);
        for (int subLength = 1; subLength < length; subLength <<= 1) { //每次从底往上归并,每次分的组上组的2倍
            ListNode prev = dummyHead, curr = dummyHead.next;
            while (curr != null) {
                ListNode head1 = curr;
                for (int i = 1; i < subLength && curr.next != null; i++) {
                    curr = curr.next;  //让cur指向当前段的最后一个节点
                }
                ListNode head2 = curr.next; //第二个组的的第一个节点
                curr.next = null; //让第一组和第二组之间断开
                curr = head2;
                for (int i = 1; i < subLength && curr != null && curr.next != null; i++) {
                    curr = curr.next; //如果还有第三组,那么cur出来for循环后cur指向第三组第一个节点,没有组了那么cur指向null
                }
                ListNode next = null; 
                if (curr != null) {  //如果有第三组
                    next = curr.next;  //让next指向第三组的第一个节点
                    curr.next = null; //让第二组和第三组之间断开联系
                }
                ListNode merged = merge(head1, head2); //合并一二组
                prev.next = merged;  //第一次while循环时候让他先指向第一个节点   //第二遍循环的时候让第二组和三组之间连接起来
                while (prev.next != null) {  //让prev走到一二组合并后的最后一个节点   第二遍循环的时候是走到第四组的最后个节点
                    prev = prev.next;
                } 
                curr = next;
            }
            //出来for循环后,这时候链表已经连接起来且是有序的
        }
        return dummyHead.next;
    }

    public ListNode merge(ListNode left, ListNode right) {
        //申请个虚拟结点,放最后返回头结点用
        ListNode dummy=new ListNode(0);
        //改指针用来走
        ListNode cur=dummy;
        while(left!=null&&right!=null){ //遍历两个链表
            if(left.val<right.val){  //如果左边的值小,就让cur.next指向左边,同时cur向后移动;;
                cur.next=left;
                cur=left;
                left=left.next;  //left向后移动
            }else{   //同理右边大
                cur.next=right;
                cur=right;
                right=right.next;
            }
        }
        if(left!=null){  //如果左边链表没空,那么cur.next直接指向left
            cur.next=left;
        }else{  //右边不为空,或为空都走下面这句话  为空直接指向null,不需要单独区分
            cur.next=right;
        }
        return dummy.next;
        }

}

总结

多自己动手实现几遍归并,你会发现熟练度会越来越高,所以许多算法打的多了就不会出现一看就会,一写就废.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值