选择排序、插入排序、冒泡排序、希尔排序
简介
以下总结几个基础的排序算法,包括选择排序、插入排序、冒泡排序、希尔排序,这几个排序算法是比较简单的几个。以下给出算法的分析和代码示例。
时间复杂度
选择排序、插入排序、冒泡排序、希尔排序四个排序算法的时间复杂度都是O(n^2)。
算法分析
选择排序
选择排序取第一个元素以此与后续的元素进行比较,保存最小的元素的下标,最终把最小的元素与第一个元素进行交换,第二次遍历取第二个元素,在剩余元素中选择最小的元素与第二个元素交换,依次类推。。。。,最终实现把所有元素按照从小到大进行排序。
以下为代码示例
template<typename T> void selectionSort(T arr[], int n){ for(int i = 0 ; i < n ; i ++){ int minIndex = i; for( int j = i + 1 ; j < n ; j ++ ) if( arr[j] < arr[minIndex] ) minIndex = j; swap( arr[i] , arr[minIndex] ); } }
选择排序是基础的排序算法,但需要重点理解,以后复杂的算法也都会以此为基础进行衍生。
插入排序
插入排序分为两种,第一种是直接插入排序,第二种是折半插入排序,以下分别描述两种排序的基本思想:
直接插入排序的基本思想:当插入第i(i>1)个元素时,前面的data[0],data[1]……data[i-1]已经排好序。这时用data[i]的排序码与data[i-1],data[i-2],……的排序码顺序进行比较,找到插入位置即将data[i]插入,原来位置上的元素向后顺序移动。
折半插入排序的基本思想:设元素序列data[0],data[1],……data[n-1]。其中data[0],data[1],……data[i-1]是已经排好序的元素。在插入data[i]时,利用折半搜索法寻找data[i]的插入位置。
因为插入排序每次循环比较可能提前退出,所以比选择排序在性能上更优,特别是对于一些近乎有序的数据,插入排序的性能更优,插入排序对小数据量排序性能更好。
以下代码为直接插入排序的示例:
template<typename T> void insertionSort(T arr[], int n) { for(int i = 1; i < n; i++) { T tmp = arr[i]; int j; for(j = i; j > 0 && arr[j - 1] > tmp; j --) arr[j] = arr[j - 1]; arr[j] = tmp; } }
冒泡排序
冒泡排序是进行两两比较,通过遍历一遍所有的数据把最大的元素放到最后,然后继续两两比较剩余的数据,依次类推,直到所有的数据都有序。冒泡排序与插入排序类似,对于几乎有序的数据,性能会更高。代码如下:
template<typename T> void bubbleSort(T arr[], int n) { int newn; do { newn = 0; for(int i = 1; i < n; i++) if(arr[i - 1] > arr[i]) { swap(arr[i - 1], arr[i]);
//记录最后一次交换的位置,在此之后的元素在下一轮比较中均不考虑 newn = i; } n = newn; }while(newn > 0); }
希尔排序
希尔排序的基本思想:
(1)希尔排序(shell sort)这个排序方法又称为缩小增量排序,是1959年D·L·Shell提出来的。该方法的基本思想是:设待排序元素序列有n个元素,首先取一个整数increment(小于n)作为间隔将全部元素分为increment个子序列,所有距离为increment的元素放在同一个子序列中,在每一个子序列中分别实行直接插入排序。然后缩小间隔increment,重复上述子序列划分和排序工作。直到最后取increment=1,将所有元素放在同一个子序列中排序为止。
(2)由于开始时,increment的取值较大,每个子序列中的元素较少,排序速度较快,到排序后期increment取值逐渐变小,子序列中元素个数逐渐增多,但由于前面工作的基础,大多数元素已经基本有序,所以排序速度仍然很快。
希尔排序的复杂度和增量序列是有关的
{1,2,4,8,...}这种序列并不是很好的增量序列,使用这个增量序列的时间复杂度(最坏情形)是O(n^2)
Hibbard提出了另一个增量序列{1,3,7,...,2^k-1},这种序列的时间复杂度(最坏情形)为O(n^1.5)
Sedgewick提出了几种增量序列,其最坏情形运行时间为O(n^1.3),其中最好的一个序列是{1,5,19,41,109,...}
示例代码如下:
template<typename T> void InsertSort(T arr[], int h, int i) { T tmp = arr[i]; int j; for(j = i; j >= h && arr[j - h] > tmp; j -= h) arr[j] = arr[j - h]; arr[j] = tmp; } template<typename T> void shellSort(T arr[], int n) { int h = 1; // 计算 increment sequence: 1, 4, 13, 40, 121, 364, 1093... while(h < n/3) h = 3 * h + 1; while(h >= 1) { //arr[h], arr[h+1], arr[h+2]....开始排序 for(int i = h; i < n; i++) { //对arr[i], arr[i-h],...进行插入排序 InsertSort(arr, h, i); } h /= 3; } }