Skip to content

Latest commit

 

History

History
98 lines (68 loc) · 3.94 KB

insert_sort.md

File metadata and controls

98 lines (68 loc) · 3.94 KB

插入排序

插入排序,一般我们指的是简单插入排序,也可以叫直接插入排序。就是说,每次把一个数插到已经排好序的数列里面形成新的排好序的数列,以此反复。

插入排序属于插入类排序算法。

除了我以外(神奇的我),有些人打扑克时习惯从第二张牌开始,和第一张牌比较,第二张牌如果比第一张牌小那么插入到第一张牌前面,这样前两张牌都排好序了,接着从第三张牌开始,将它插入到已排好序的前两张牌里,形成三张排好序的牌,后面第四张牌继续插入到前面已排好序的三张牌里,直至排序完。

一、算法介绍

举个简单例子,有 4 个元素的数列:4 2 9 1,我们使用插入排序:

[]表示排好序

第一轮: [4] 2 9 1 拿待排序的第二个数 2插入到排好序的数列 [4]
    与排好序的数列 [4] 比较
    第一轮进行中2  4 插入到 4 

第二轮: [2 4] 9 1 拿待排序的第三个数 9插入到排好序的数列 [2 4]
    与排好序的数列 [2 4] 比较
    第二轮进行中9  4 不变化

第三轮: [2 4 9] 1 拿待排序的第四个数 1插入到排好序的数列 [2 4 9]
    与排好序的数列 [2 4 9] 比较
    第三轮进行中1  9 插入到 9 
    第三轮进行中1  4 插入到 4 
    第三轮进行中1  2 插入到 2 

结果: [1 2 4 9]

最好情况下,对一个已经排好序的数列进行插入排序,那么需要迭代 N-1 轮,并且因为每轮第一次比较,待排序的数就比它左边的数大,那么这一轮就结束了,不需要再比较了,也不需要交换,这样时间复杂度为:O(n)

最坏情况下,每一轮比较,待排序的数都比左边排好序的所有数小,那么需要交换 N-1 次,第一轮需要比较和交换一次,第二轮需要比较和交换两次,第三轮要三次,第四轮要四次,这样次数是:1 + 2 + 3 + 4 + ... + N-1,时间复杂度和冒泡排序、选择排序一样,都是:O(n^2)

因为是从右到左,将一个个未排序的数,插入到左边已排好序的队列中,所以插入排序,相同的数在排序后顺序不会变化,这个排序算法是稳定的。

二、算法实现

package main

import "fmt"

func InsertSort(list []int) {
	n := len(list)
	// 进行 N-1 轮迭代
	for i := 1; i <= n-1; i++ {
		deal := list[i] // 待排序的数
		j := i - 1      // 待排序的数左边的第一个数的位置

		// 如果第一次比较,比左边的已排好序的第一个数小,那么进入处理
		if deal < list[j] {
			// 一直往左边找,比待排序大的数都往后挪,腾空位给待排序插入
			for ; j >= 0 && deal < list[j]; j-- {
				list[j+1] = list[j] // 某数后移,给待排序留空位
			}
			list[j+1] = deal // 结束了,待排序的数插入空位
		}
	}
}

func main() {
	list := []int{5}
	InsertSort(list)
	fmt.Println(list)

	list1 := []int{5, 9}
	InsertSort(list1)
	fmt.Println(list1)

	list2 := []int{5, 9, 1, 6, 8, 14, 6, 49, 25, 4, 6, 3}
	InsertSort(list2)
	fmt.Println(list2)
}

输出:

[5]
[5 9]
[1 3 4 5 6 6 6 8 9 14 25 49]

其实,就是通过将右边的一个数 deal ,找到它的左边 已排好序的数列 位置,然后插进去。

数组规模 n 较小的大多数情况下,我们可以使用插入排序,它比冒泡排序,选择排序都快,甚至比任何的排序算法都快。

数列中的有序性越高,插入排序的性能越高,因为待排序数组有序性越高,插入排序比较的次数越少。

大家都很少使用冒泡、直接选择,直接插入排序算法,因为在有大量元素的无序数列下,这些算法的效率都很低。

附录

代码下载: https://github.com/hunterhug/goa.c/blob/master/code/sort