插入排序的工作原理
插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序。
因为每次都是在已排好序的数列中找可以插入的合适位置,所以叫做插入排序。
下面👇我画了一张图,来描述选择排序的基本过程。
- 首先,准备一个乱序的整型数组,
[6,3,5,7,1,9,2,8,4],我们对其进行从小到大的排序; - 先进行第一次循环,3和6进行比较,3比6小,所以3应该插在6的前面,因为我们是原地排序,所以就让3和6进行交换即可;
- 接着再看3前面还有没有元素,没有,所以本轮循环结束,得到了
[3,6,5,7,1,9,2,8,4],前两个数字是有序的了; - 接着再比较5和6,5比6小,同样的,5应该插入6的前面,所以5和6就行交换,得到
[3,5,6,7,1,9,2,8,4]; - 再然后,5前面还有元素,继续向前比较,发现5比3大,所以本轮循环就结束了;
- 后面的元素按照上述步骤依次执行;
- 最终,得到一个从小到大顺序排列的数组,
[1,2,3,4,5,6,7,8,9]。
代码实现(golang)
package main
import "fmt"
// InsertSort 插入排序
func InsertSort(in []int) {
inLen := len(in)
for i := 0; i < inLen-1; i++ {
tmp := in[i+1]
j := i + 1
for j > 0 && tmp < in[j-1] {
in[j] = in[j-1]
j--
}
if i+1 != j {
in[j] = tmp
}
}
}
// 交换数据
func swap(in []int, i int, j int) {
tmp := in[i]
in[i] = in[j]
in[j] = tmp
}
func main() {
arr := []int{6, 3, 5, 7, 1, 9, 2, 8, 4}
InsertSort(arr)
fmt.Println(arr)
}
以上就是最简单的插入排序的代码实现。
根据图解中的流程,我们可以看到arr[4]=1这个元素在进行排序时,和前面的4个元素都进行了三个步骤,分别是:比较、交换、移动指针,这其中也有优化的点,那就是可以将交换放到最后,也就是说,用一个临时变量tmp来存储arr[4]=1的值,大致过程是这样的:
- 1和前一个元素7比较,7比1大,则7移动到原来1的位置,7的位置就空出来了;
- 1再和前一个元素5比较,5比1大,则5移动到原来7的位置,5的位置就空出来了;
- 1再和前一个元素3比较,3比1大,则3移动到原来5的位置,3的位置就空出来了;
- 1再和前一个元素6比较,6比1大,则6移动到原来3的位置,6的位置就空出来了;
- 最后,将tmp的值赋值给
arr[0]。 以上,就可以节省一些交换以及移动指针的步骤。具体代码实现如下:
package main
import "fmt"
// InsertSort 插入排序
func InsertSort(in []int) {
inLen := len(in)
for i := 0; i < inLen-1; i++ {
tmp := in[i+1]
lastIndex := i + 1
for j := i + 1; j > 0 && tmp < in[j-1]; j-- {
in[j] = in[j-1]
lastIndex = j - 1
}
if lastIndex != i+1 {
in[lastIndex] = tmp
}
}
}
// 交换数据
func swap(in []int, i int, j int) {
tmp := in[i]
in[i] = in[j]
in[j] = tmp
}
func main() {
arr := []int{6, 3, 5, 7, 1, 9, 2, 8, 4}
InsertSort(arr)
fmt.Println(arr)
}
时间复杂度
插入排序因为也进行了两次循环,所以它的时间复杂度也是O(n²)。