算法学习100天——4 归并排序

例题1:

题目地址(25. 合并两个排序的链表)

https://leetcode-cn.com/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof/

题目描述

输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

示例1:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

限制:
0 <= 链表长度 <= 1000

注意:本题与主站 21 题相同:https://leetcode-cn.com/problems/merge-two-sorted-lists/

代码

Java Code:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
/**
 * 这个题算是归并排序的子题目,只实现一次合并
 * 思路:
 * 将两个链表(数组)依次遍历,时间复杂度O(N)
 * 任意一个链表(数组)遍历结束后,将另一个链表(数组)直接并入
 */
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode root = new ListNode(-1);
        ListNode res = root; 
        while(l1 != null && l2 != null){
            if(l1.val <= l2.val){
                root.next = new ListNode(l1.val);
                root = root.next;
                l1 = l1.next;
            }else {
                root.next = new ListNode(l2.val);
                root = root.next;
                l2 = l2.next;
            }
        }
        // 如果l1 还没走完
        if(l1 != null){
            root.next = l1;
        }
        // 如果l2 还没走完
        if(l2 != null){
            root.next = l2;
        }

        return res.next;
    }
}

执行结果

执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户

内存消耗:41.4 MB, 在所有 Java 提交中击败了17.81%的用户

例题2:

题目地址(剑指 Offer II 077. 链表排序)

https://leetcode-cn.com/problems/7WHec2/

题目描述

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

示例 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


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

注意:本题与主站 148 题相同:https://leetcode-cn.com/problems/sort-list/

代码

Java Code:

/**
 * 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; }
 * }
 */

/**
 * 思路:
 * 排序题拿到,立马想到归并排序,因为归并排序的时间复杂度为O(NlogN);
 * 
 * 这个题的难点在于链表,不能像数组一样直接二分,
 * 第一种思路(没实现)
 * 将链表先转换为数组O(N),再归并排序O(NlogN),再将数组转换为链表O(N),最终为O(NlogN)
 * 
 * 第二种思路
 * 就利用链表实现归并排序,用到快慢指针法将链表分成两端
 * 
 */

class Solution {
    public ListNode sortList(ListNode head) {
        // 递归出口
        if(head == null || head.next == null ){
            return head;
        }

        // 快慢指针,将数组分为段,
        // 每一次执行,fast指针走两个,slow走一个,
        // 执行完毕后,fast在链表的末端,或者倒数第二位置,slow在中点或中点前一个位置
        ListNode fast = head;
        ListNode slow = head;
        while(fast.next != null && fast.next.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        // 找到中点
        ListNode mid = slow.next;
        // 截断,head和mid各领一段
        slow.next = null;

        // 递归调用
        ListNode left = sortList(head);
        ListNode right = sortList(mid);
        // 返回 合并后的链表
        return merge(left, right);
    }

    private ListNode merge(ListNode left, ListNode right){
        ListNode head = new ListNode();
        ListNode res = head;
        while(left != null && right != null){
            if(left.val <= right.val){
                head.next = new ListNode(left.val);
                left = left.next;
            }else {
                head.next = new ListNode(right.val);
                right = right.next;
            }
            // 赋值之后,往后移动一个,为下一个做准备
            head = head.next;
        }
         // 如果left没走完
        if(left != null){
            head.next = left;
        }

        // right 没走完
        if(right != null){
            head.next = right;
        }
        return res.next;
    }
}

执行结果

执行用时:7 ms, 在所有 Java 提交中击败了46.40%的用户

内存消耗:48.6 MB, 在所有 Java 提交中击败了29.73%的用户

posted @ 2022-02-28 23:38  浪漫主义程序员  阅读(33)  评论(0编辑  收藏  举报