冒泡排序,选择排序和插入排序的总结
在规模较小时,或者元素的有序性较高时,插入排序的时间复杂度可以接近 O(n) ,是上述三种排序里表现最好的
一、通过表格我们可以发现,冒泡排序的时间复杂度是要优于选择排序的,但实际上选择排序的平均效率要优于冒泡排序
冒泡排序的思想是不断比较相邻元素大小,如果逆序,就需要不断的交换位置,也就是说在冒泡排序的一次子循环中,可能需要进行多次交换操作
选择排序的思想是在未排序序列中找出最小值,添加到有序序列末端,并不断重复。这种排序方式导致选择排序无论如何都会进行 n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n−1) 次比较操作,但是在交换位置时,选择排序只需要进行一步操作
也就是说,对于非优化的冒泡排序和选择排序,他们都需要进行 n n n 次子循环。而在每一次子循环中,冒泡排序需要 n n n 次比较和多次数据交换,选择排序需要 n n n 次比较和一次数据交换
所以选择排序的实际效率一般会略高于冒泡排序
二、从字面上来看,冒泡排序和插入排序的时间复杂度是一样的,但是插入排序的效率高于冒泡排序
要回答这个问题,首先要了解有序度
有序度是列表中有序元素对的个数,用数学关系表示就是
a
[
i
]
≤
a
[
j
]
,
i
f
i
<
j
a[i] \leq a[j], if \quad i < j
a[i]≤a[j],ifi<j
例如,对于[2, 4, 3, 1, 5, 6]这个数组来说,有序元素对个数是 11,分别是:
(2,4)(2,3)(2,5)(2,6)
(4,5)(4,6)
(3,5)(3,6)
(1,5)(1,6)
(5,6)
对于一个倒序数组来说,有序度是 0; 对于一个完全有序数组来说,有序度是 \frac{n(n-1)}{2},在这里就是 15
逆序度的定义和有序度正好相反,因此我们可以得到一个公式
逆 序 度 = 满 有 序 度 − 有 序 度 逆序度 = 满有序度 - 有序度 逆序度=满有序度−有序度
我们排序的过程就是一个增加有序的,减少逆序度的过程,最后达到满有序度,就说明排序完成了
对于冒泡排序来说,每交换一次,有序度+1, 因此总交换次数等于逆序度。对于包含 n n n 个数据的冒泡排序来说,最好的情况是逆序度为0,即不需要交换;最坏的情况是逆序度为 n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n−1)。换句话说,平均情况下,冒泡排序需要 n ( n − 1 ) 4 \frac{n(n-1)}{4} 4n(n−1) 次交换操作
对于插入排序来说,每一次操作中后移元素的个数之和就等于逆序度,见下图:
逆序度 = 10 = 3 + 3 + 4
再来回答问题,我们之前提到比较排序算法的时间复杂度有两个因素:比较次数和交换次数
非优化的冒泡排序的比较次数一定是大于插入排序的,优化过后两者比较次数不太好比较,但最大值都是 n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n−1)
因为冒泡排序的交换次数等于逆序度,插入排序的元素移动次数(这里的元素后移其实也是靠交换赋值来实现的,但是形象上看更像是将元素向后移动了,也便于和冒泡排序区分,所以表述成移动次数)也等于逆序度。但是,从代码实现来看,冒泡排序的交换操作要比插入排序的移动复杂
冒泡排序每交换一次,需要给三个变量赋值
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
而插入排序只需要一次,且插入排序的临时变量只会在每一轮子循环中交换一次值
arr[pos+1] = arr[pos];
因此,插入排序的实际效率是比冒泡排序要高的
相关章节
第一节 简述
第二节 稀疏数组 Sparse Array
第三节 队列 Queue
第四节 单链表 Single Linked List
第五节 双向链表 Double Linked List
第六节 单向环形链表 Circular Linked List
第七节 栈 Stack
第八节 递归 Recursion
第九节 时间复杂度 Time Complexity
第十节 排序算法 Sort Algorithm
第十一节 冒泡排序 Bubble Sort
第十二节 选择排序 Select Sort
第十三节 插入排序 Insertion Sort
第十四节 冒泡排序,选择排序和插入排序的总结
第十五节 希尔排序 Shell’s Sort
第十六节 快速排序 Quick Sort
第十七节 归并排序 Merge Sort