前言
本题由于没有合适答案为以往遗留问题,最近有时间将以往遗留问题一一完善。
147. 对链表进行插入排序
不积跬步,无以至千里;不积小流,无以成江海,Swift社区 伴你前行。如果大家有建议和意见欢迎在文末留言,我们会尽力满足大家的需求。
难度水平:中等
摘要
在本篇文章中,我们将探讨如何使用插入排序算法对单链表进行排序。插入排序是一种简单的排序算法,它的基本思想是:通过将未排序的元素逐步插入到已排序的部分,最终形成一个有序的序列。我们将结合Swift代码实现单链表的插入排序,并通过示例测试展示如何应用该算法。
描述
给定单个链表的头 head
,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。
插入排序 算法的步骤:
- 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
- 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
- 重复直到所有输入数据插入完为止。
下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。
对链表进行插入排序。
示例 1:
输入: head = [4,2,1,3]
输出: [1,2,3,4]
示例 2:
输入: head = [-1,5,3,4,0]
输出: [-1,0,3,4,5]
提示:
- 列表中的节点数在
[1, 5000]
范围内 -5000 <= Node.val <= 5000
题解答案
插入排序的实现步骤:
- 定义辅助节点:创建一个虚拟节点
dummy
,作为已排序部分的头节点,便于插入操作。 - 遍历链表:对于链表中的每一个元素,遍历已排序部分,找到合适的位置插入。
- 调整指针:在适当位置插入当前节点时,调整节点指针,保持链表的结构。
Swift实现
class ListNode {
var val: Int
var next: ListNode?
init(_ val: Int) {
self.val = val
self.next = nil
}
}
func insertionSortList(_ head: ListNode?) -> ListNode? {
// 创建虚拟头节点
let dummy = ListNode(0)
var current = head // 当前待排序节点
while current != nil {
var prev = dummy // prev指向已排序部分的最后一个节点
// 在已排序部分找到合适的插入位置
while let nextNode = prev.next, nextNode.val < current!.val {
prev = nextNode
}
let nextTemp = current!.next // 保存当前节点的下一个节点
// 插入当前节点
current!.next = prev.next
prev.next = current
current = nextTemp // 移动到下一个待排序节点
}
return dummy.next // 返回排序后的链表头
}
题解代码分析
-
创建虚拟头节点:我们通过创建一个虚拟的头节点
dummy
来简化插入操作。虚拟头节点的作用是避免在链表头部插入时需要特殊处理。 -
插入操作:
prev
指针用于找到已排序部分中小于current
节点的最大节点(即找到current
应该插入的位置)。- 然后,我们将
current
节点插入到prev.next
的位置,调整链表指针来实现插入操作。
-
遍历链表:每次遍历链表,都会将一个待排序节点插入到已排序部分。操作直到链表全部排序。
-
时间复杂度:每次插入操作都需要遍历已排序部分,最多需要插入
n
个节点,因此时间复杂度是O(n^2)
,其中n
是链表的节点数量。 -
空间复杂度:插入排序算法只需要常数级的额外空间,因此空间复杂度是
O(1)
。
示例测试及结果
示例 1
let head1 = ListNode(4)
head1.next = ListNode(2)
head1.next?.next = ListNode(1)
head1.next?.next?.next = ListNode(3)
let sortedList1 = insertionSortList(head1)
printList(sortedList1) // 输出:[1, 2, 3, 4]
示例 2
let head2 = ListNode(-1)
head2.next = ListNode(5)
head2.next?.next = ListNode(3)
head2.next?.next?.next = ListNode(4)
head2.next?.next?.next?.next = ListNode(0)
let sortedList2 = insertionSortList(head2)
printList(sortedList2) // 输出:[-1, 0, 3, 4, 5]
辅助函数:打印链表
func printList(_ head: ListNode?) {
var current = head
while current != nil {
print(current!.val, terminator: " ")
current = current?.next
}
print()
}
时间复杂度
-
时间复杂度:由于每次插入一个节点需要遍历已排序部分,因此总时间复杂度为
O(n^2)
,其中n
是链表节点数。 -
空间复杂度:插入排序只用了常数级的额外空间,因此空间复杂度为
O(1)
。
总结
插入排序是一种简单且易于实现的排序算法,尤其适用于链表的排序。在这篇文章中,我们演示了如何通过插入排序算法对链表进行排序,并通过Swift代码实现了该算法。虽然插入排序的时间复杂度为O(n^2)
,它的空间复杂度是O(1)
,因此对于较小的链表,插入排序是一种不错的选择。
关于我们
我们是由 Swift 爱好者共同维护,我们会分享以 Swift 实战、SwiftUI、Swift 基础为核心的技术内容,也整理收集优秀的学习资料。