Leetcode 148. 排序链表(快排)

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

解法:
   链表版快排,它属于不稳定排序
   先获得并提取随机中心,
   然后以中心节点为头,从头到尾循环当前链表,
   小于中心的插头,大于中心的插尾,等于中心的插入 leftPivot 和 rightPivot 中间(减少相同的元素的递归)
   接着将新的头尾节点连接上原有节点,
   最后递归左右俩区间
   注意:添加虚拟节点,提取出来的随机节点要最后才指向空,如果新尾结点从没有移动过则需要随着 rightPivot 移动
  时间复杂度为 O(n*logn);空间复杂度:O(logn)
代码:

    /**
     * 链表版快排(升序)
     * 时间复杂度为 O(n*logn);空间复杂度:O(logn)
     */
    private ListNode solution(ListNode head) {
        // 判空
        if (head == null || head.getNext() == null) {
            return head;
        }

        int len = getLen(head);
        System.out.println(len);

        // 头结点前的虚拟节点,方便处理头接地以及获取最新的头结点
        ListNode virtual = new ListNode(0, head);
        // 快排核心算法
        quickSort(virtual, head, null, len);
        return virtual.getNext();
    }

    private int getLen(ListNode head) {
        int len = 0;
        while (head != null) {
            len++;
            head = head.getNext();
        }
        return len;
    }

    /**
     * 快排核心算法:
     * 先获得并提取随机中心,
     * 然后以中心节点为头,从头到尾循环当前链表,
     * 小于中心的插头,大于中心的插尾,等于中心的插入 leftPivot 和 rightPivot 中间(减少相同的元素的递归)
     * 接着将新的头尾节点连接上原有节点,
     * 最后递归左右俩区间
     * 注意:添加虚拟节点,提取出来的随机节点要最后才指向空,如果新尾结点从没有移动过则需要随着 rightPivot 移动
     *
     * @param preHead 当前链表头结点之前的节点
     * @param head 当前链表头结点(含)
     * @param tail 当前链表尾结点(不含)
     * @param len 节点元素个数
     */
    private void quickSort(ListNode preHead, ListNode head, ListNode tail, int len) {
        if (len <= 1) {
            return;
        }

        // 获得并提取随机中心
        ListNode[] pivot = getPivot(head, len);
        head = pivot[0];
        ListNode rightPivot = pivot[1];
        System.out.println(head + ":" + rightPivot);

        // 从头到尾循环链表,小于中心的插头,大于中心的插尾,等于中心的插入 rightPivot 后面,rightPivot 后移(减少相同的元素的递归)
        ListNode leftPivot = rightPivot;
        ListNode virtual = new ListNode(0, rightPivot);
        ListNode newTail = rightPivot;
        int leftLen = 0;
        int rightLen = 0;

        for (ListNode now = head; now != tail;) {
            ListNode next = now.getNext();

            if (now.getVal() < leftPivot.getVal()) {
                // 头插
                now.setNext(virtual.getNext());
                virtual.setNext(now);
                leftLen++;
            } else if (now.getVal() > leftPivot.getVal()) {
                // 尾插
                now.setNext(newTail.getNext());
                newTail.setNext(now);
                newTail = now;
                rightLen++;
            } else {
                now.setNext(rightPivot.getNext());
                rightPivot.setNext(now);
                rightPivot = now;
                // 如果新尾结点从没有移动过则需要随着 rightPivot 移动
                if (newTail.getNext() == rightPivot) {
                    newTail = rightPivot;
                }
            }

            now = next;
        }

        // 将新的头尾节点连接上原有节点
        newTail.setNext(tail);
        preHead.setNext(virtual.getNext());
        System.out.println("head:" + head + " tail:" + tail + " len:" + len);
        System.out.println("leftPivot:" + leftPivot + " rightPivot:" + rightPivot);
        System.out.println("virtual:" + virtual + " newTail:" + newTail);
        System.out.println("leftLen:" + leftLen + " rightLen:" + rightLen);

        // 递归左右俩区间
        quickSort(preHead, preHead.getNext(), leftPivot, leftLen);
        quickSort(rightPivot, rightPivot.getNext(), newTail.getNext(), rightLen);
    }

    /**
     * 获得并提取随机中心
     * 注意:添加虚拟节点,提取出来的节点要最后才指向空
     * @return 返回提取后的节点数组(0-提取后的新头结点,1-提取出来的节点)
     */
    private ListNode[] getPivot(ListNode head, int len) {
        ListNode[] pivot = new ListNode[2];

        // 添加虚拟节点,方便操作头结点
        ListNode virtual = new ListNode(0, head);
        ListNode pivotNode = virtual;

        int pivotNum = (new Random()).nextInt(len);
        for (int i = 0; i < pivotNum; i++) {
            pivotNode = pivotNode.getNext();
        }

        // 提取随机中心节点
        ListNode temp = pivotNode.getNext();
        pivotNode.setNext(pivotNode.getNext().getNext());
        // 提取出来的节点要最后才指向空
        temp.setNext(null);

        pivot[0] = virtual.getNext();
        pivot[1] = temp;
        return pivot;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值