什么是插入排序?
插入排序是一种简单而直观的排序算法,它的工作方式类似于我们手动排序卡片或整理文件:
工作原理:
初始状态:将数组分为两部分:一部分是已排序的(最开始时这部分只包含第一个元素),另一部分是未排序的(包含数组的其余部分)。
排序过程:
取元素:从未排序的部分取出第一个元素,这个元素在逻辑上是"手中拿着的牌"。
寻找位置:将这个元素与已排序部分的元素从后向前依次比较。
插入动作:如果"手中的牌"比比较的元素小(对于升序排序),则将比较的元素向后移动一位,为插入新元素腾出空间。
放置元素:当找到一个已排序的元素小于或等于"手中的牌",或者已经比较到了已排序部分的第一个元素时,将"手中的牌"放在这个位置上。
重复过程:重复上述过程,每次将未排序部分的第一个元素插入到已排序部分的正确位置,直到未排序部分没有元素为止。
插入排序的工作原理
让我们通过一个具体的例子来说明插入排序的工作原理:
假设我们有一个未排序的数列 [7, 2, 4, 9, 3]
,我们希望对其进行升序排序。
- 开始排序:最初,我们将第一个元素(7)视为已排序的序列。
- 第一轮排序
- 取出下一个元素(2),与已排序序列(7)从后向前比较。
- 由于2小于7,我们将7向后移动一位,使得2占据第一个位置。数组变为
[2, 7, 4, 9, 3]
。
- 第二轮排序
- 取出4,与已排序序列(2, 7)从后向前比较。
- 4大于2,但小于7,因此我们将7向后移动一位,4插入到7之前。数组变为
[2, 4, 7, 9, 3]
。
- 后续轮次
- 重复上述过程,每次取出未排序部分的第一个元素,将其插入到已排序序列的正确位置。
- 排序完成
- 经过几轮排序后,整个数组变为有序,最终序列为
[2, 3, 4, 7, 9]
。
- 经过几轮排序后,整个数组变为有序,最终序列为
插入排序的代码示例(Python)重点重点重点讲解
代码中的相关注释一定要好好看
python
def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
#插入排序是从后往前比较的
#当前选中的元素与其前一个比较,如果有序,则不需要调换位置
#如果无序,则需要换位置
j = i-1 #这一行代码就是去到位置i的前一个元素的位置
#当j>=0不用解释,索引不能够为负数
#key是位置j前面的元素,判断a[j]与a[j-1]的大小,如果不是有序的就要调整位置
while j >=0 and key < arr[j]:
#到了while内部说明不是有序的,需要调整,把大的往后移
arr[j+1] = arr[j]
j -= 1
#举个例子[2,3,1],1的位置为2,j就应该是2-1=1,检测到2比1小,赋值[2,3,3],这时候j=j-1,j=0,1与2相比还是小,在挪一下
#[2,2,3],到头了,j=-1,说明碰到最坏的情况了移动到头了,但还好排完顺序了,退出while
arr[j+1] = key
#刚刚是不是到了[2,2,3],把1放进去[1,2,3],搞定一次排序
# 测试数组
arr = [7, 2, 4, 9, 3]
insertion_sort(arr)
print("Sorted array is:", arr)
在这个代码示例中,insertion_sort
函数实现了插入排序算法。它逐一将未排序部分的元素插入到已排序部分的正确位置。
插入排序的时间复杂度
最佳情况 - O(n)
在最佳情况下,即输入数组已经是完全有序的情况下,每个元素无需移动,只需进行一次比较即可确定其位置。
python
markdownCopy code最佳情况时间复杂度:
- 对于每个元素,仅需进行一次比较。
- 总比较次数 = 1 + 1 + ... + 1 = n 次
- 时间复杂度 = O(n)
平均情况和最坏情况 - O(n^2)
在平均情况或最坏情况(输入数组完全逆序)下,每次插入可能需要在已排序的部分中比较和移动所有元素。
python
平均情况和最坏情况时间复杂度:
- 第1个元素需要比较1次,第2个元素最多比较2次,...,第n个元素最多比较n次。
- 总比较次数 = 1 + 2 + 3 + ... + n
- 使用等差数列求和公式:总比较次数 = n * (n + 1) / 2 ≈ n^2 / 2
- 时间复杂度 = O(n^2)
插入排序的空间复杂度
插入排序是一种原地排序算法,不需要额外的存储空间,除了输入数组外,它仅需要一个额外的存储空间来暂存当前要插入的元素。
python
空间复杂度:
- 需要常数级别的额外空间
- 空间复杂度 = O(1)
插入排序的优势
- 简单直观 :
- 插入排序的算法非常直接------就像人们整理手中的扑克牌一样,逐一选择元素并将其放置在适当的位置。这种直观性使得插入排序易于理解和实现,特别是对于编程新手而言。
- 由于其简单性,插入排序经常被用作教学初级排序算法的典型例子。
- 适合小数据集 :
- 在处理小型数据集时,插入排序可以非常高效,尤其是当输入数组大部分已经排序时。在这种情况下,插入排序的实际性能可以接近 O(n)。
- 对于较小的数据集,其实现的简单性可能超过了其他更复杂排序算法的微小性能优势。
- 稳定排序 :
- 插入排序是一种稳定的排序算法,这意味着它会保留相等元素的初始顺序。这在某些应用中是非常重要的,比如当主键相同时保持记录的相对顺序。
插入排序的劣势
- 效率较低 :
- 对于大规模的数据集,插入排序的效率通常不如其他更高级的排序算法,如快速排序、归并排序或堆排序。这是因为其平均和最坏情况的时间复杂度都是 O(n^2),导致在数据量增大时性能急剧下降。
- 在最坏的情况下(即输入数组完全逆序),每次插入都可能需要比较和移动所有已排序元素,导致效率极低。
- 不适合大规模数据集 :
- 当处理大量数据时,插入排序的性能不足可能成为一个严重的缺点。在这种情况下,时间复杂度较低的算法(如 O(n log n))会显著优于插入排序。
- 对于大数据集,插入排序的大量元素移动操作会导致显著的性能下降。
结语
总的来说,插入排序因其简单性和在特定情况下的高效性而受到欢迎,尤其适用于数据量小或部分有序的数据集。然而,由于其在处理大规模数据集时的性能限制,它通常不适用于更复杂的应用场景,其中可能会选择更高效的排序算法。了解插入排序的这些优势和劣势可以帮助开发者在面对不同的排序需求时作出更合适的算法选择。
下一期讲解希尔排序,相当于升级版插入排序