【LeetCode 148. 排序链表】归并排序和快速排序
148. 排序链表
快速排序
用快速排序时,将中间节点当作pivot比结尾节点快,下图分别为中间节点和尾节点的运行结果。
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode tail = head;
while (tail.next != null) {
tail = tail.next;
}
return quickSort(head, tail);
}
public ListNode quickSort(ListNode head, ListNode tail) {
ListNode saveTail = tail.next;
ListNode smallDummy = new ListNode(-1), largeDummy = new ListNode(-1), equalDummy = new ListNode(-1);
// 注意equal的节点不止一个
ListNode small = smallDummy, large = largeDummy, equal = equalDummy;
// 找中点
ListNode fast = head, slow = head;
// 注意结尾不是null而是tail.next
while (fast != saveTail && fast.next != saveTail) {
fast = fast.next.next;
slow = slow.next;
}
while (true) {
ListNode tmp = head.next;
head.next = null;
if (head.val == slow.val) {
equal.next = head;
equal = equal.next;
} else if (head.val < slow.val) {
small.next = head;
small = small.next;
} else {
large.next = head;
large = large.next;
}
if (head == tail) break;
head = tmp;
}
small.next = equalDummy.next;
equal.next = largeDummy.next;
// 注意把尾巴接上
large.next = saveTail;
ListNode node1 = small, node2 = large;
if (small == smallDummy) {
node1 = smallDummy.next;
} else {
node1 = quickSort(smallDummy.next, small);
}
if (large == largeDummy) {
node2 = largeDummy.next;
} else {
node2 = quickSort(largeDummy.next, large);
}
// 注意拼接
equal.next = node2;
return node1;
}
}
归并(自顶向下)
提交之后发现自顶向下的结果比自底向上的快2ms。
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode pre = getMid(head), mid = pre.next;
// 注意要断开左右两边的链表
pre.next = null;
ListNode l1 = sortList(head);
ListNode l2 = sortList(mid);
return merge(l1, l2);
}
public ListNode getMid(ListNode head) {
// slow指向了中点或者右中点
// 所以要记下slow之前的指针
ListNode fast = head, slow = head, pre = null;
// 因为每次左右两边都断开了, 所以tail为null
while (fast != null && fast.next != null) {
pre = slow;
fast = fast.next.next;
slow = slow.next;
}
return pre;
}
public ListNode merge(ListNode l1, ListNode l2) {
ListNode dummyNode = new ListNode(-1), cur = dummyNode;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
cur.next = l1;
l1 = l1.next;
} else if (l1.val > l2.val) {
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
// 注意把剩下的链表接上
cur.next = l1 == null ? l2 : l1;
return dummyNode.next;
}
}
归并(自底向上)
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) return head;
int length = 0;
ListNode cur = head;
while (cur != null) {
cur = cur.next;
length++;
}
ListNode dummyNode = new ListNode(-1, head);
for (int subLength = 1; subLength < length; subLength = subLength << 1) {
ListNode prev = dummyNode;
// 注意cur要是每次排序后的新的head
cur = dummyNode.next;
while (cur != null) {
ListNode head1 = cur;
// 注意要判断cur.next!=null
for (int i = 1; i < subLength && cur != null && cur.next != null; i++) {
cur = cur.next;
}
// 如果上面不判断cur.next!=null
// for结束条件为cur==null, cur.next会报错
ListNode head2 = cur.next;
// 断开两个链表
cur.next = null;
// 重新赋值给cur
cur = head2;
// 找到下一个开始位置
for (int i = 1; i < subLength && cur != null && cur.next != null; i++) {
cur = cur.next;
}
//断开subLength*2之后的位置
ListNode next = null;
// 这里的cur可能为null(当head2为空时)
if (cur != null) {
next = cur.next;
cur.next = null;
}
// 合并
ListNode merged = merge(head1, head2);
prev.next = merged;
// 移动到subLength*2之后的位置
while (prev.next != null) prev = prev.next;
cur = next;
}
}
return dummyNode.next;
}
public ListNode merge(ListNode l1, ListNode l2) {
ListNode dummyNode = new ListNode(-1), cur = dummyNode;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
cur.next = l1;
l1 = l1.next;
} else if (l1.val > l2.val) {
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
// 注意把剩下的链表接上
cur.next = l1 == null ? l2 : l1;
return dummyNode.next;
}
}
分类:
算法题
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律