【LeetCode】排序链表

#LeetCode每日一题【链表专题】

  • 排序链表
    https://leetcode-cn.com/problems/sort-list/

  • 分析
    链表经典排序算法,归并排序和快速排序;两者都是采用“分治”的思想,将整体不断的切分成两部分,直至最小。且时间复杂度都是O(n*logn),空间复杂度O(1)(不同于数组需要开辟新空间);
    不同点是:归并排序是先切分再排序合并,快速排序是先排序在切分。

  • 实现
    归并排序:

    1. 使用递归不断的将链表分成两部分,直至长度为1(归)
    2. 在切分结束之后,不断的对两个有序链表进行合并,使之有序(并)
    

    快速排序:

    1. 每次设定一个基准值,通过处理将左边的值都小于基准值、右边的值都大于基准值;如此的递归处理左右两部分
    2. 处理完之后,整体自然是有序的
    
  • 代码
    归并排序:

 /*
   链表排序经典算法:归并排序
   1. 不断地将链表拆分成两段,直至长度为1:      递归
   2. 再对两个有序列表进行合并排序              合并

   时间复杂度:O(n*logn) 空间复杂度 O(1)
    */
    public ListNode sortList02(ListNode head) {
        return merge(head);
    }

    private ListNode merge(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        // 获取链表中点、需要指定一个pre指针,对应慢指针的上一个
        ListNode slow = head, fast = head, prev = null;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            prev = slow;
            slow = slow.next;
        }
        prev.next = null;
        ListNode left,right;
        // 递归处理,不断地拆分成两段
        left = merge(head);
        right = merge(slow);
        // 该递归函数需要返回 将左右排好序的新链表
        return sort(left, right);
    }

    // 排序两个有序链表
    private ListNode sort(ListNode left, ListNode right) {
        // 排序两个有序链表
        ListNode p = new ListNode(0);
        ListNode pTemp = p;
        while (left != null && right != null) {
            if (left.val > right.val) {
                pTemp.next = right;
                right = right.next;
            } else {
                pTemp.next = left;
                left = left.next;
            }
            pTemp = pTemp.next;
        }
        // 左右可能某个存在空
        if (left != null) {
            pTemp.next = left;
        }
        if (right != null) {
            pTemp.next = right;
        }
        return p.next;
    }

LeetCode耗时:5ms
在这里插入图片描述

快速排序:

/*
   链表排序经典算法:快速排序
    1. 每次设定一个基准值,通过处理将左边的值都小于基准值、右边的值都大于基准值;如此的递归处理左右两部分
    2. 处理完之后,整体自然是有序的

   时间复杂度:O(n*logn) 空间复杂度 O(1)
    */
public ListNode sortList04(ListNode head) {
        quickSort(head, null);
        return head;
    }

    /*
    优化,不改变head指向,即递归程序返回值为null
    添加一个参数,表示需要处理的链表区间
     */
    private void quickSort(ListNode head, ListNode end) {
        // 快速排序需要找到基准值的合适位置,即使得其左边值都比他小、右边值都比他大
        // 在链表中使用快慢指针去寻找:慢指针留下来作为标记、快指针每次向前,当快指针小于基准值,交换快和慢的下一个指针位置(保证大的在后面,小的在前面),最后将基准值与慢指针位置进行交换
        if (head == null || head.next == end) {
            return;
        }
        ListNode slow = head, fast = head;
        ListNode basic = head;
        while (fast != end) {
            if (fast.val < basic.val) {
                slow = slow.next;
                int temp = fast.val;
                fast.val = slow.val;
                slow.val = temp;
            }
            fast = fast.next;
        }
        if (slow != basic) {
            // 交换slow和basic,此时即满足条件
            int temp = slow.val;
            slow.val = basic.val;
            basic.val = temp;
        }
        // 右半部分
        fast = slow.next;
        quickSort(head, slow.next);
        quickSort(fast, end);
    }

快速排序另一种写法:(带返回值的)

/*
   链表排序经典算法:快速排序
    1. 每次设定一个基准值,通过处理将左边的值都小于基准值、右边的值都大于基准值;如此的递归处理左右两部分
    2. 处理完之后,整体自然是有序的

   时间复杂度:O(n*logn) 空间复杂度 O(1)
    */
    public ListNode sortList03(ListNode head) {
        return quickSort(head);
    }

    private ListNode quickSort(ListNode head) {
        // 快速排序需要找到基准值的合适位置,即使得其左边值都比他小、右边值都比他大
        // 在链表中使用快慢指针去寻找:慢指针留下来作为标记、快指针每次向前,当快指针小于基准值,交换快和慢的下一个指针位置(保证大的在后面,小的在前面),最后将基准值与慢指针位置进行交换
        if (head == null || head.next == null) {
            return head;
        }
        ListNode slow = head, fast = head;
        ListNode basic = head;
        while (fast != null) {
            if (fast.val < basic.val) {
                slow = slow.next;
                int temp = fast.val;
                fast.val = slow.val;
                slow.val = temp;
            }
            fast = fast.next;
        }
        if (slow != basic) {
            // 交换slow和basic,此时即满足条件
            int temp = slow.val;
            slow.val = basic.val;
            basic.val = temp;
        }
        // 右半部分
        fast = slow.next;
        // 左半部分
        slow.next = null;
        ListNode left = quickSort(head);
        ListNode right = quickSort(fast);
        // 递归之后需要返回处理好的链表,需要首尾拼接下
        // 也可以设计没有返回值的递归程序,即不改变head指向,多传一个参数标识一下位置即可;
        ListNode t = left;
        while (t.next != null) {
            t = t.next;
        }
        t.next = right;
        return left;
    }

LeetCode耗时:1338ms
在这里插入图片描述
(有点震惊,比归并排序慢了几百倍…)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值