前言
插入排序是很重要的排序,著名的希尔排序就是从插入排序演变过来的,所以我们需要并且很多时候有些面试也是会面试插入排序的,所以需要好好捋清楚插入排序的逻辑是什么
插入排序gif
插入排序单趟实现
1,插入排序我们需要假设最后一个数值也就是end+1是需要排序的,其他都是排序好的
2,把end+1这个下标的数值存放在tmp里面,并且和前面进行比较
3,如果遇见的元素比tmp大,我们继续往前移动进行比较,同时a[end]=a[end+1]往后覆盖
4,当遇见的是比tmp小的数值的时候,此时我们找到了tmp数值应该在的位置,进行插入
插入排序注意事项
这里需要注意的关键也是区间问题,假设数组有n个,那么end就是倒数第二个下标,end+1就是最后一个下标,是为了防止越界
我们需要小于n-1,因为,end=n-1;end+1=n,那么就越界了
所以在循环最大值里面,end=i=n-2,;end+1=n-1(最后一个数值)
插入排序代码的实现
//插入排序 void InsertionSort(int* a, int n) { //多趟实现,这里n的截止条件-1,是因为下标从n-1就结束了, //不过我们需要小于n-1,因为,end=n-1;end+1=n,那么就越界了 for (int i = 0; i < n - 1; i++) { //单趟实现 int end = i; int tmp = a[end + 1]; while (end >= 0) { //判断是不是比前一个数值小,小的话就往前走,不小的话停下来进行赋值 if (tmp < a[end]) { a[end + 1] = a[end]; end--; } else//找到了,此时跳出循环就可以 { break; } } //这里是很多人会搞混乱的一点, //因为是找到了,所以end还没有继续移动,但是end+1+1的元素已经移动,所以end+1的位置是tmp应该出现的位置 a[end + 1] = tmp; } }
解释:
函数
InsertionSort
接受两个参数:一个指向整数数组a
的指针和数组的长度n
。外层循环从索引
0
遍历到n-1
。每次迭代,i
代表已排序部分的最后一个元素的索引。在外层循环的每次迭代中,
end
变量被设置为当前索引i
,表示当前考虑的元素的索引。tmp
变量存储了a[i + 1]
的值,这是未排序的第一个元素,也是我们准备插入到已排序部分的元素。内层
while
循环用于在已排序部分从后向前扫描,找到tmp
应该插入的位置。end
变量随着比较逐步递减。在
while
循环中,如果tmp
小于当前比较的元素a[end]
,则将a[end]
向后移动一个位置,为tmp
腾出空间。如果
tmp
大于或等于a[end]
,则while
循环通过break
语句结束,找到了tmp
应该插入的位置。循环结束后,将
tmp
赋值给a[end + 1]
,完成插入操作。这个过程重复进行,直到数组中的所有元素都被扫描并插入到正确的位置。
代码逻辑:
- 插入排序的基本思想是,对于数组中的每个元素,将其插入到前面已经排好序的子数组中的正确位置。
- 初始时,认为数组的第一个元素是已排序的。然后,从第二个元素开始,逐个插入到前面的已排序序列中。
- 每次插入操作都需要将元素与已排序序列中的元素进行比较,直到找到合适的插入点。
注意:
- 这段代码在插入元素时,如果插入点是数组的开始,那么不需要进行任何移动操作,直接插入即可。
- 代码中的
end
变量用于记录当前比较的元素在数组中的位置,而tmp
变量用于暂存当前要插入的元素。- 插入排序是稳定的排序算法,因为它不会改变相等元素的相对顺序。
性能:
- 插入排序的平均时间复杂度和最坏时间复杂度都是 𝑂(𝑛^2),其中 n 是数组的长度。
- 插入排序的空间复杂度是 𝑂(1),因为它是原地排序算法,不需要额外的存储空间。
插入排序的时间复杂度
插入排序算法的时间复杂度取决于数组的初始顺序,具体如下:
最佳情况:如果输入数组已经是完全有序的,插入排序只需要进行 n 次比较(每次比较后插入一个元素到已排序部分),而不需要进行任何交换。在这种情况下,时间复杂度是O(n)。
平均情况:在平均情况下,插入排序的时间复杂度是 O(n^2)。这是因为每个元素都需要与已排序部分的多个元素进行比较,平均下来,每个元素需要比较n/2次。
最坏情况:如果输入数组是完全逆序的,插入排序需要进行n(n−1)/2次比较和 n(n−1)/2次交换,时间复杂度是 O(n^2)。
空间复杂度:插入排序是原地排序算法,它只需要一个额外的存储空间来暂存当前比较的元素,因此空间复杂度是 O(1)。
稳定性:插入排序是稳定的排序算法,它保持了相等元素的原始顺序。
时间复杂度的详细分析:
- 插入排序通过构建有序序列来工作,对于未排序的数据,在已排序的序列中从后向前扫描,找到相应位置并插入。
- 在每次迭代中,算法将当前元素与已排序序列中的元素逐一比较,找到合适的插入点。
- 对于每个元素,比较操作可能需要进行 i 次(其中 𝑖i 是当前元素在数组中的位置),从第一个元素到最后一个元素,所需比较的总次数是递增的。
时间复杂度的数学表达式是:
总比较次数=1+2+3+…+(𝑛−1)=𝑛(𝑛−1)/2总比较次数
这表明插入排序的时间复杂度是 Θ(𝑛^2),尽管在最坏情况下时间复杂度较高,插入排序对于小规模数据集或部分有序的数据集来说是非常高效的。