148. 排序链表(中)
题目
- 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
python
法一、冒泡排序
- 冒泡排序:两个for循环,i从头开始,j在i后一位开始,比较如果j小于i就交换,否则i往后移
class Solution:
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
i = head
j = head.next
if not head or head.next is None: # 如果链表为空或只有一个节点,直接返回链表本身
return []
p = ListNode(0)# 创建一个虚拟节点作为排序后链表的头节点
while i and i.next:
while j and j.next:
if j.val < i.val:
p.next, j.next = i.next, i
else:# 如果j大于等于i,则继续向后移动j节点
j = j.next
i = i.next
return p.next# 返回排序后链表的头节点
- 超时 :时间复杂度:O(n2);空间复杂度:O(1)
- 分析:
不适合的排序:基数排序(只适合非负数)、希尔排序(链表不支持随机访问,该排序涉及步长)、堆排序(树适合用顺序结构)
超时排序:冒泡排序、选择排序、插入排序、快速排序。
空间换时间:归并排序、计数排序、桶排序
法二、归并排序
class Solution:
def merge(self, left, right):
# 归并环节
dummy_head = ListNode(-1)#构造一个头节点
cur = dummy_head#用 cur 指向 dummy_head 用于遍历
while left and right:#比较两个链表头节点 left 和 right 的值大小。将较小的头节点加入到合并后的链表中。并向后移动该链表的头节点指针。
if left.val <= right.val:
cur.next = left
left = left.next
else:
cur.next = right
right = right.next
cur = cur.next
if left:
cur.next = left
elif right:
cur.next = right
return dummy_head.next
def mergeSort(self, head: ListNode):
# 分割环节
if not head or not head.next:
return head
# 快慢指针找到中心链节点
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 断开左右链节点
left_head, right_head = head, slow.next
slow.next = None
# 归并操作
return self.merge(self.mergeSort(left_head), self.mergeSort(right_head))
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
return self.mergeSort(head)
javascript
归并排序(分治)
// 876. 链表的中间结点(快慢指针)
function middleNode(head) {
let slow = head, fast = head;
// 先找到链表的中间结点的【前一个节点】
while (fast.next && fast.next.next) {
slow = slow.next;
fast = fast.next.next;
}
const mid = slow.next; // 下一个节点就是链表的中间结点 mid
slow.next = null; // 断开 mid 的前一个节点和 mid 的连接
return mid;
}
// 21. 合并两个有序链表(双指针)
function mergeTwoLists(list1, list2) {
const dummy = new ListNode(); // 用哨兵节点简化代码逻辑
let cur = dummy; // cur 指向新链表的末尾
while (list1 && list2) {
if (list1.val < list2.val) {
cur.next = list1; // 把 list1 加到新链表中
list1 = list1.next;
} else { // 注:相等的情况加哪个节点都是可以的
cur.next = list2; // 把 list2 加到新链表中
list2 = list2.next;
}
cur = cur.next;
}
cur.next = list1 ?? list2; // 拼接剩余链表
return dummy.next;
}
var sortList = function(head) {
// 如果链表为空或者只有一个节点,无需排序
if (head === null || head.next === null) {
return head;
}
// 找到中间节点,并断开 head2 与其前一个节点的连接
// 比如 head=[4,2,1,3],那么 middleNode 调用结束后 head=[4,2] head2=[1,3]
let head2 = middleNode(head);
// 分治
head = sortList(head);
head2 = sortList(head2);
// 合并
return mergeTwoLists(head, head2);
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效