[链表]leetcode147-对链表进行插入排序和leetcode148-链表排序

[链表]–对链表进行插入排序和链表排序


题目链接

leetcode 147.对链表进行插入排序

题目

对链表进行插入排序。
插入排序算法:

  1. 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
  2. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
  3. 重复直到所有输入数据插入完为止。

示例

输入: 4->2->1->3
输出: 1->2->3->4

输入: -1->5->3->4->0
输出: -1->0->3->4->5

解析

  1. 首先 head 不能是 null;
  2. 有可能插入到 head 之前,所以定义一个 dummyHead,用于指向新的头节点;
  3. 比较之前,需要保存需要的节点,待排序节点 cur(初始为head.next) 以及其前驱节点 prevCur(有序序列的最后一个节点);
  4. 插入节点:
    (1)首先如果 cur > prevCur,说明 cur 大于前面的所有结点,请继续遍历;
    (2)反之,需要查找插入位置:从 temp=DummyHead 开始查找,因为插入时需要插入位置的前一个节点,比较 temp.next 和 cur 的大小关系;
    (3)找到之后就准备插入待排序节点,在此之前需要 prevCur=cur.next; 用于保存下一个待排序节点;
  5. cur = prevCur.next 继续遍历直至为 null。

代码实现

public class Solution147 {

    /**
     * Definition for singly-linked list
     */
    class ListNode {
        int val;
        ListNode next;
        public ListNode(int val) {
            this.val = val;
        }
    }

    /**
     * 1.判空
     * 2.定义一个 dummyHead 作为傀儡头结点, 因为有可能插入在 head 之前, 插入节点是需要前驱节点的, 同时, 用来指向新的头节点;
     * 3.定义一个 prevCur 指向待排序节点的前驱节点, 也就是有序序列的最后一个节点;
     * 4.遍历链表进行插入,由于第一个 head 可以看作有序, 所以从 head.next 出发;
     * 5.首先如果 cur 大于 prevCur, 说明 cur 大于前面所有的,直接两个指针后移就好;
     * 6.反之说明需要进行比较然后插入了:
     *      (1)由于插入需要前驱节点,所以在比较时, 从 temp=dummyHead 出发,比较 temp.next.val 和 cur.val 的大小;
     *      (2)查找到第一个不小于 cur 的节点停止;
     *      (3)插入在 temp 后面, 再插入之前需要注意将 cur.next 保存起来,需要进行下轮排序(直接 prevCur.Next=cur.next);
     * 7.让 cur 指向下一个待排序节点继续遍历链表直至完结。
     */
    public ListNode insertionSortList(ListNode head) {
        // 排空
        if (head == null) return null;
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode cur = head.next;// 待排序结点
        ListNode prevCur = head;// 指向待排序节点的前一个结点
        while (cur != null) {
            // cur 大于前面所有的结点
            if (prevCur.val < cur.val) {
                prevCur = cur;
                cur = cur.next;
            }else {
                ListNode temp = dummyHead;
                // 查找第一个不小于 cur 的前驱节点
                while (temp.next.val < cur.val) {
                    temp = temp.next;
                }
                // 插入
                prevCur.next = cur.next;
                cur.next = temp.next;
                temp.next = cur;
                // 指向下一个待排序结点
                cur = prevCur.next;
            }
        }
        return dummyHead.next;
    }
}
题目链接

leetcode 148.排序链表

题目

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

示例

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

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

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

解析

插入排序,也可但意义不大;而且时间复杂度O(n^2)
需要时间复杂度O(n log n),也就是快排、堆排、归并三个,比较适合链表的就是归并,正好可以使用 合并两个有序链表

  1. 计算链表长度,归并排序需要将链表拆分成子链表进行排序;
  2. subLen 作为每次子链表的长度,初始为1(有序链表长度),每次是前一次的二倍;
  3. 将原链表分割成若干个长度为 subLen 的子链表(最后一组可能不够),两个一组进行合并(合并两个有序链表);合并之后有序链表的长度变成了subLen*2(最后一组可能不够);
  4. subLen变成前一次的二倍,继续分割合并直至整个链表排序完毕。

代码实现

public class Solution148 {

    /**
     * Definition for singly-linked list
     */
    class ListNode {
        int val;
        ListNode next;
        public ListNode(int val) {
            this.val = val;
        }
    }

    /**
     * 自底向上的归并排序
     */
    public ListNode sortList(ListNode head) {
        if (head == null) return null;
        ListNode cur = head;
        int length = 0;
        // 链表长度
        while (cur != null) {
            length++;
            cur = cur.next;
        }
        // 归并
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        for (int subLen = 1; subLen < length; subLen *= 2) {
            ListNode prev = dummyHead;// 已排序的尾结点
            cur = dummyHead.next;
            while (cur != null) {
                // 链表1
                ListNode head1 = cur;
                for (int i = 0; i < subLen-1 && cur.next != null; i++) {
                    cur = cur.next;
                }
                // 链表2
                ListNode head2 = cur.next;
                cur.next = null;// 断开链表 1
                cur = head2;
                for (int i = 0; i < subLen-1 && cur != null && cur.next != null; i++) {
                    cur = cur.next;
                }
                // 判断是否有链表2
                ListNode curNext = null;
                if (cur != null) {
                    curNext = cur.next;
                    cur.next = null;// 断开链表2
                }
                ListNode temp = merge(head1, head2);
                prev.next = temp;
                // prev 再次指向已排序的尾结点
                while (prev.next != null) {
                    prev = prev.next;
                }
                cur = curNext;
            }
        }
        return dummyHead.next;
    }

    /**
     * 合并两个有序链表
     */
    public ListNode merge(ListNode head1, ListNode head2) {
        ListNode dummyHead = new ListNode(0);
        ListNode temp = dummyHead;
        // 比较并插入到新链表
        while (head1 != null && head2 != null) {
            if (head1.val <= head2.val) {
                temp.next = head1;
                temp = temp.next;
                head1 = head1.next;
            }else {
                temp.next = head2;
                temp = temp.next;
                head2 = head2.next;
            }
        }
        // 此时一个链表为 null
        temp.next = (head1 == null) ?  head2 : head1;
        return dummyHead.next;
    }
}

-----------------------------------------------------------------------------有始有终分割线----------------------------------------------------------------------------------

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值