【牛客题霸——模板速刷TOP101】:BM12 单链表的排序(难度:【中等】)

【牛客题霸——模板速刷TOP101】:BM12 单链表的排序(难度:【中等】)


如果有其他更好的方法,欢迎在评论区留言~


题目描述

给定一个节点数为n的无序单链表,对其按升序排序。

数据范围:0<n≤100000,保证节点权值在[-109,109]之内。
要求:空间复杂度 O(n),时间复杂度 O(nlogn)

知识点

链表、排序

示例

输入:
[1,3,2,4,5]
返回值:
{1,2,3,4,5}
输入:
[-1,0,-2]
返回值:
{-2,-1,0}

解题过程

方法一:转化为数组排序

具体做法:

  • 遍历链表并将链表结点存储到数组 array中
  • 通过对 array 进行排序,实现链表结点的排序
  • 构建新链表结点 res,遍历数组 array,拼接新的返回链表
/*
 * function ListNode(x){
 *   this.val = x;
 *   this.next = null;
 * }
 */

/**
 * 
 * @param head ListNode类 the head node
 * @return ListNode类
 */
function sortInList( head ) {
    // write code here
    var array=[];
    while(head!=null){
        array.push(head.val);
        head=head.next;
    }
    array.sort(function (a,b) {return a-b});         //数组排序
    var res=new ListNode(-1),temp=res;     //重新构造新链表结点
    for(var i=0;i<array.length;i++){       //遍历数组,将数组中元素添加到新的链表中
        var arr=new ListNode(array[i]);
        temp.next=arr;
        temp=temp.next;
    }
    return res.next;
}
module.exports = {
    sortInList : sortInList
};
复杂度分析
  • 时间复杂度O(NlogN):N表示链表结点数量,遍历链表O(N),数组排序(NlogN),遍历数组O(N)
  • 空间复杂度O(N):使用额外数组占用空间O(N)
方法二:归并排序(递归)

具体做法:

  • 分割 cut 环节: 找到当前链表中点,并从中点将链表断开(以便在下次递归 cut 时,链表片段拥有正确边界);使用 fast,slow 快慢双指针法,奇数个节点找到中点,偶数个节点找到中心左边的节点(slow=head,fast=head.next), 找到中点 slow 后,执行 slow.next = null 将链表切断。

递归分割时,输入当前链表左端点 head 和中心节点 slow 的下一个节点 temp(因为链表是从 slow 切断的)。

cut 递归终止条件: 当head.next = null时,说明只有一个节点了,直接返回此节点

  • 合并 merge 环节: 将两个排序链表合并,转化为一个排序链表。

双指针法合并,建立辅助ListNode nHead 作为头部。
设置两指针 left, right 分别指向两链表头部,比较两指针处节点值大小,由小到大加入合并链表头部,指针交替前进,直至添加完两个链表。
返回辅助ListNode nHead 作为头部的下个节点 nHead.next。

/*
 * function ListNode(x){
 *   this.val = x;
 *   this.next = null;
 * }
 */

/**
 * 
 * @param head ListNode类 the head node
 * @return ListNode类
 */
function sortInList( head ) {
    // write code here

    if(head==null || head.next==null){
        return head;
    }
    var slow=head,fast=head.next;        	 // 使用快慢指针寻找链表的中点
    while(fast!=null && fast.next!=null){
        slow=slow.next;
        fast=fast.next.next;
    }
    var temp=slow.next;
    slow.next=null;              			//将链表切断
    var left=sortInList(head);         		 // 递归左右两边进行排序
    var right=sortInList(temp);
    var nHead=new ListNode(-1),res=nHead;
    while(left!=null && right!=null){
        if(left.val<right.val){        				// left  right链表循环对比
            res.next=left;
            left=left.next;
        }
        else{
            res.next=right;
            right=right.next;
        }
        res=res.next;
    }
    res.next=left!=null ? left : right;     // 最后添加未对比的链表部分判断左链表是否为空
    return nHead.next;
}
module.exports = {
    sortInList : sortInList
};
复杂度分析
  • 时间复杂度O(NlogN):N表示链表结点数量,二分归并算法O(NlogN)
  • 空间复杂度O(1):仅使用常数级变量空间
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值