算法学习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%的用户