Insertion 插入排序,不断将未排好的数插入到已经排好的序列中。和冒泡排序类似,都属于古老的基本排序算法。它的原理并不复杂,只需要找出插入的位置即可。我们用LeetCode上的算法题,感受一下它的原理。
Insertion 插入排序算法
INSERTION
英 [ɪnˈsɜːʃn] 美 [ɪnˈsɜːrʃn]
插入排序;插入;插入法;刊载;刊登
tion/ion 是名词后缀,像这类词还有很多
| insert | 插入 | + ion | insertion |
| educate | 教育 | 去e + ion | education |
| graduate | 毕业 | 去e + ion | graduation |
| instruct | 指示 |
ion | instruction |
| suggest | 建议 | ion | suggestion |
| collect | 收集 | ion | collection |
插入排序,不断将未排好的数插入到已经排好的序列中。和冒泡排序类似,都属于古老的基本排序算法。它的原理并不复杂,只需要找出插入的位置即可。在数组中使用插入排序可以通过改变下班实现。但我在 LeetCode 上遇到这道插入排序的算法题,规定了只能使用单向链表的数据结构。加大了插入的难度。知难而上,遇到了就解决掉。
LeetCode
这是 LeetCode 的第 147 题:
对链表进行插入排序。
插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
插入排序算法:
插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。
示例 1:输入: 4->2->1->3输出: 1->2->3->4示例 2:输入: -1->5->3->4->0输出: -1->0->3->4->5
题目的内容到这里就结束了。很好理解,思路也很清晰。但是,一顿操作猛如虎,一看提交不通过。这题,我调试了很多次才成功。之所以犯错就是对链表的指针理解不深入。
以下是我的解题思路:
第一步:定义一个虚拟节点。用于输出结果。这里不能用原始 head 节点。是因为原链表的第一个节点会因为排序导致位置变化,这会导致输出的结果不完整。
如,输入 head:8=>7=>9=>6。经过程序排序处理后,输出的结果却是 8=>9。
val dummy = ListNode()dummy.next = head
第二步:定义两个节点。分别是 preNode 和 currNode。preNode 作为排序后的尾节点,也是排序后链表中的最大值。currNode 用来被比较和遍历链表。
var preNode = headvar currNode = head.next
第三步:检查 currNode 与 preNode 的值大小比较。若 currNode 大,我们只需要把 currNode 赋值给 preNode。反之就需要进行插入排序。
while (currNode != null) {if (preNode!!.`val` <= currNode.`val`) {preNode = currNodecurrNode = currNode.next} else {}}
第四步:插入排序的核心是找出需要插入的节点位置。通过 while 需要遍历找出小于 currNode 节点的位置。
var temp: ListNode = dummywhile (temp.next != currNode&& temp.next!!.`val` < currNode.`val`) {temp = temp.next!!}
第五步:插入节点。交换位置前需要把当前节点剔除。然后把较大值作为当前节点的下一个节点。
while (currNode != null) {if (preNode!!.`val` <= currNode.`val`) {} else {var temp: ListNode = dummywhile (temp.next != currNode&& temp.next!!.`val` < currNode.`val`) {temp = temp.next!!}preNode.next = currNode.nextcurrNode.next = temp.nexttemp.next = currNodecurrNode = preNode.next}}
举例说明,若 head -1=>10=>-2=>2=>5,在第二次开始遍历的情况如下:
preNode : 10=>-2=>2=>5
currNode : -2=>2=>5
temp : 0=>-1=>10=>-2=>2 =>5
执行 preNode.next=currNode.next代码,将当前节点 -2 剔除掉。剔除后的结果如下。
preNode:10=>2=>5
temp:0=>-1=>10=>2=>5
执行 currNode.next=temp.next 代码,断开 temp 和 -1 节点的连接,并将 -2 节点放在 -1 节点之前。
currNode:-2=>-1=>10=>2=>5
执行 temp.next=currNode 代码,重连 temp 和 -2 节点的连接。
temp:0=>-2=>-1=>10=>2=>5
执行 currNode=preNode.next 代码,开始下一个节点遍历。
完整代码如下:
fun main(args: Array<String>) {headNode = ListNode()generateListNode(headNode, 5)printListNode(headNode?.next)printListNode(insertionSortList(headNode?.next))}fun insertionSortList(head: ListNode?): ListNode? {if (head?.next == null) return headval dummy = ListNode()dummy.next = headvar preNode = headvar currNode = head.nextwhile (currNode != null) {if (preNode!!.`val` <= currNode.`val`) {preNode = currNodecurrNode = currNode.next} else {var temp: ListNode = dummywhile (temp.next != currNode&& temp.next!!.`val` < currNode.`val`) {temp = temp.next!!}preNode.next = currNode.nextcurrNode.next = temp.nexttemp.next = currNodecurrNode = preNode.next}}return dummy.next}var headNode: ListNode? = nullvar nodeIndex: Int = 1class ListNode {var `val`: Int = 0var next: ListNode? = null}fun generateListNode(node: ListNode?, num: Int) {if (nodeIndex <= num) {val newNode = ListNode()newNode.`val` = (-1 * num..2 * num).random()newNode.next = nullnode?.next = newNodenodeIndex++generateListNode(newNode, num)}}fun printListNode(node: ListNode?) {if (node == null) {println()return}var nextNode: ListNode? = node.nextvar symbol = " => "var printStr = "${node.`val`}$symbol"while (nextNode != null) {if (nextNode.next == null) {symbol = ""}printStr += "${nextNode.`val`}$symbol"nextNode = nextNode.next}println(printStr)}
文章到这里就结束了,insertion "插入排序" 你会了么?
学英语会编程|ITDragon博客
