初等排序
- 插入排序
- 冒泡排序
- 选择排序
稳定排序
- 所谓稳定排序,就是指在存在两个及以上相同的元素时,在排序处理前后,相对的前后位置不发生改变。
插入排序
-
插入排序,顾名思义,核心思想就是把一个数插入到这个数组中它应该在的位置。从第二个数开始,将前面的看做已经排好序的数列,我们只需让它不断地向前比较,(以升序排列为例)只要找到第一个比它小的数,并将它插入在这个数的后面,然后重复这个过程直到所有的数都在它应该在的位置上,排序就完成了。
-
动画演示 (图片取自VisuAlgo)
-
代码
void insertionSort(int *array, int N){ //N为数组array的大小
for (int i = 1; i < N; i++){ //从第二个数开始比较到N-1结束
int temp = array[i]; //记录当前需要排序的数
int j = i - 1;
//当j大于等于0的时候,一直向前寻找直到找到比当前需要排序的数小的数
while (j >= 0 && array[j] > temp){
array[j+1] = array[j]; //将比要排序的数大的排到当前数之后
j--;
}
array[j+1] = temp; //最后将要排序的数插入到它应该去的位置
}
}
- 很容易可以看出,插入排序是稳定的。在程序中只要遇到相等,即
(array[ j ] == temp)的情况,while循环就会跳出,不会改变相等的元素的相对位置。 - 对于插入排序来说,时间复杂度为O(n2),但是数据排列的顺序对排序的时间影响很大。对于降序排列的数列来说,时间复杂度为O(n2),但是对于已经升序排好的数列,时间复杂度为O(n),所以插入排序适合用在相对来说较为有序的数列上。
冒泡排序
-
冒泡排序的核心思想就是从一列数中找出最大的那个,然后像冒泡一样上浮,将相邻两个数较大的放在后面,最终选出最大的数,将其放在最后,然后再对除了最后一个数的前面部分数列重复这个过程,依次找出第二大,第三大……的数,并将其依次排列,即可得到排序。
-
动画演示 (图片取自VisuAlgo)
-
代码
void bubbleSort(int *array, int N){
for (int i = 0; i < N - 1; i++){ //长度为N的数组要至少循环N-1次
for (int j = 0; j < N - i; j++){ //最后的i个数为已经排好的数,所以只排列前N-i个数
if (array[j] > array[j+1]) //如果前面的数比后一个数大,就将他们交换
swap(array[j], array[j+1]);
}
}
}
- 和插入排序一样,因为没有交换两个相同的数,即条件为(array[ j ] > array[ j+1 ]),所以冒泡排序也是稳定排序。冒泡排序也为双循环,故时间复杂度应该为O(n2).
选择排序
- 选择排序的思想和冒泡排序有点像,都是找出一列数中的最值,然后放到最前或者最后。但与冒泡排序不同的是,冒泡排序是两个相邻的数进行交换,而选择排序则是遍历选择出最值然后直接进行交换。
- 动画演示 (图片取自VisuAlgo)
- 代码
void seletionSort(int *array, int N){
for (int i = 0; i < N - 1; i++){ //和冒泡排序一样,交换N-1次
int min = array[i], min_; //用min记录当前数组最小值,用min_记录最小值下标
for (int j = i; j < N; j++){ //对从i开始到结束的数组选择最小值
if (array[j] < min){ //记录最小值及其下标
min = array[j];
min_ = j;
}
}
swap(array[min_],array[i]); //将选择出的最小值与这个数组的第一位交换
}
}
- 对于选择排序来说,因为是直接交换的最小值,可能存在两个及以上的相同元素没有按照相对位置来排序,所以选择排序是不稳定排序。同冒泡排序一样,选择排序的时间复杂度也是O(n2)。