选择排序
思路:
从最左边的元素开始,逐个遍历,找到这一趟中的最小值,存到minIndex里面,最后这一趟结束,将它和这一趟开始遍历的位置i上的元素swap,直到最后一个位置上也就位。相当于选择出每一趟最小的元素,然后把它放到这一趟开始的地方。
基础版:
void selectionSort(int arr[], int n){
for(int i = 0 ; i < n ; i ++){
// 寻找[i, n)区间里的最小值
int minIndex = i;
for( int j = i + 1 ; j < n ; j ++ )
if( arr[j] < arr[minIndex] )
minIndex = j;
swap( arr[i] , arr[minIndex] );
}
}
插入排序
思路:
由于第一个元素可以默认为已排好序,因此可以从第二个元素开始遍历,每一趟使一个元素正确的插入到前面已排好序的数组中。如果这个元素比它前面的元素小,则swap一下,如果大于等于它前面的元素,说明它以及它前面的元素已经排好序了,这一趟结束。
插入排序和选择排序一个很大的区别就是插入排序每一趟有可能提前结束,即它只要到达一个位置大于等于它前面的元素,那么这一趟就已经排好了;而选择排序每一趟必须遍历完这一趟所有的元素,找到最小的那个,然后再把最小的那个和这一趟开始的元素进行swap,是不能提前结束的。
对于近乎有序的数组,适合用插入排序。
对于完全有序的数组,插入排序复杂度为 O(n) O ( n ) 。
基础版:
void insertionSort(T arr[], int n){
for( int i = 1 ; i < n ; i ++ ) {
// 寻找元素arr[i]合适的插入位置
// 写法1
for( int j = i ; j > 0 ; j-- ) //注意这里j遍历到下标为1的位置就好了,因为下面是将位置j和位置j-1的元素进行比较
if( arr[j] < arr[j-1] )
swap( arr[j] , arr[j-1] );
else
break;
// 写法2
// for( int j = i ; j > 0 && arr[j] < arr[j-1] ; j -- )
// swap( arr[j] , arr[j-1] );
}
return;
}
改进版:
将原本一次次的swap操作,变成一次次比较,而在一趟的最后进行赋值。
插入排序算法针对完全排好序的数组是O(n)级别的算法,每次判断一下当前数就是正确位置,这一趟就退出了
改进后的插入排序避免了很多次unnecessary copy,由此非常适合基本排好序而不需要很多次变动的数组。
void insertionSort(T arr[], int n){
for( int i = 1 ; i < n ; i ++ ) {
// 写法3
T e = arr[i];// 声明一个变量保存要插入的元素
int j; // j保存元素e应该插入的位置
f