vlambda博客
学习文章列表

Insertion 插入排序算法

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 上遇到这道插入排序的算法题,规定了只能使用单向链表的数据结构。加大了插入的难度。知难而上,遇到了就解决掉。




Insertion 插入排序算法

LeetCode


这是 LeetCode 的第 147 题:


对链表进行插入排序。

Insertion 插入排序算法

插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。


插入排序算法:

  1. 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。

  2. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。

  3. 重复直到所有输入数据插入完为止。

 

示例 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 = currNode currNode = 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 = dummy while (temp.next != currNode  && temp.next!!.`val` < currNode.`val`) { temp = temp.next!! } preNode.next = currNode.next currNode.next = temp.next temp.next = currNode currNode = 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 head val dummy = ListNode() dummy.next = head var preNode = head var currNode = head.next while (currNode != null) { if (preNode!!.`val` <= currNode.`val`) { preNode = currNode currNode = currNode.next } else { var temp: ListNode = dummy while (temp.next != currNode && temp.next!!.`val` < currNode.`val`) { temp = temp.next!! } preNode.next = currNode.next currNode.next = temp.next temp.next = currNode currNode = preNode.next } }
return dummy.next}
var headNode: ListNode? = nullvar nodeIndex: Int = 1
class ListNode { var `val`: Int = 0 var next: ListNode? = null}
fun generateListNode(node: ListNode?, num: Int) { if (nodeIndex <= num) { val newNode = ListNode() newNode.`val` = (-1 * num..2 * num).random() newNode.next = null node?.next = newNode nodeIndex++ generateListNode(newNode, num) }}
fun printListNode(node: ListNode?) { if (node == null) { println() return } var nextNode: ListNode? = node.next var symbol = " => " var printStr = "${node.`val`}$symbol" while (nextNode != null) { if (nextNode.next == null) { symbol = "" } printStr += "${nextNode.`val`}$symbol" nextNode = nextNode.next } println(printStr)}


文章到这里就结束了,insertion "插入排序" 你会了么?




Insertion 插入排序算法
Insertion 插入排序算法



学英语会编程|ITDragon博客

每周一、三、五更新
长按二维码关注