插入排序:简单地说,就是就将无序序列依次插入到有序序列中。
算法描述:
1. 从第一个元素开始,该元素可以认为已经被排序
2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5. 将新元素插入到该位置后
6. 重复步骤2~5
时间复杂度:
最坏情况;O(n^2);
平 均:O(n^2);
/*********** 插入排序 ************/ void InsertSort(int *p, const int len) { assert(p != NULL); int i; int j; int temp; for (i = 1; i < len; i++) { temp = p[i]; for (j = i-1; j >= 0; j--) { if (p[i] > temp) { p[j+1] = p[j]; } else { break; } } p[j+1] = temp; } }
冒泡排序:
一种简单地排序算法。
算法描述:
1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这样会使得最后的元素为最大值;
3. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
时间复杂度:
最坏情况;O(n^2);
平 均:O(n^2);
/*********** 冒泡排序 ************/ void BubbleSort(int *p, const int len) { assert(p != NULL); int i; int j; int temp; for (i = 0; i < len; i++) { for (j = 0; j < len-i-1; j++) { if (p[j] > p[j+1]) { temp = p[j]; p[j] = p[j+1]; p[j+1] = temp; } } } } /*********** 冒泡排序 ************/ void BubbleSort(int *p, const int len) { assert(p != NULL); int i; int j; int temp; for (i = 0; i < len; i++) { for (j = i+1; j < len; j++) { if (p[i] > p[j]) { temp = p[i]; p[i] = p[j]; p[j] = temp; } } } }
选择排序:从未排序列中找到最大(小)元素将其置于合适的位置。
算法描述:
1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
2. 从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。
3. 重复步骤2,直到所有元素均排序完毕。
时间复杂度:
最坏情况;O(n^2);
平 均:O(n^2);
/*********** 选择排序 ************/ void SelectSort(int *p, const int len) { assert(p != NULL); int i; int j; int k; int temp; for (i = 0; i < len-1; i++) { k = i; for (j = i+1; j < len; j++) { if (p[k] > p[j]) { k = j; } } if (k != i) { temp = p[i]; p[i] = p[k]; p[k] = temp; } } }
快速排序:通过一趟排序将待排记录分隔成独立的两部分,其中以部分记录的关键字比另一部分记录的关键字小,
则可分别对这两部分记录继续进行排序,以达到整个序列有序。
算法描述:
1. 设置两个变量low、high,排序开始的时候:low=0,high=N-1;
2. 以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3. 从high开始向前搜索,即由后开始向前搜索(high--),找到第一个小于key的值p[high],将值为key的项与p[high]交换;
4. 从low开始向后搜索,即由前开始向后搜索(low++),找到第一个大于key的p[low],将值为key的项与p[low]交换;
5. 重复第3、4、5步,直到low=high; (3,4步中,没找到符合条件的值,即3中p[high]不小于key,4中p[low]不大于key的时候改变low、high的值,使得high=high-1,low=low+1,直至找到为止。找到符合条件的值,进行交换的时候low,high指针位置不变。另外,low==high时令循环结束)。
时间复杂度:
最坏情况:O(n^2);
平 均:O(nlogn);
/*********** 调整分区 ************/ int Partition(int *p, const int low, const int high) { assert(p != NULL); int i = low; int j = high; int key = p[low]; while (i < j) { while (i < j && key <= p[j]) { j--; } if (i < j) { p[i++] = p[j]; } while (i < j && key > p[i]) { i++; } if (i < j) { p[j--] = p[i]; } } p[i] = key; return i; } /*********** 快速排序 ************/ void QuickSort(int *p, const int low, const int high) { assert(p != NULL); int pos = Partition(p, low, high); if (low < high) { QuickSort(p, low, pos-1); // 递归地对关键字前面的序列进行排序 QuickSort(p, pos+1, high); // 递归地对关键字后面的序列进行排序 } }
堆排序:是指利用堆这种数据结构所设计的一种排序算法。
算法思想:
1. 初始化操作:将R[1..n]构造为“大顶堆”;
2. 将当前无序区的堆顶记录R[0]和该序列的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆);
3. 直到所有元素有序;
时间复杂度:
最坏情况;O(nlogn);
平 均:O(nlogn);
/*********** 构建大顶堆 ************/ void AdjustTop(int *p, const int len) { assert(p != NULL); int pos = len / 2 - 1; int t; int temp; while (pos >= 0) { if (pos*2+2 == len) // 只有自由左子树的情况 { t = p[pos*2+1] > p[pos] ? pos*2+1 : pos; } else // 左右子树都存在 { t = p[pos*2+1] > p[pos*2+2] ? pos*2+1 : pos*2+2; t = p[t] > p[pos] ? t : pos; } if (t != pos) // 加此条件判断以减少交换次数 { temp = p[pos]; p[pos] = p[t]; p[t] = temp; } pos--; } } /*********** 堆排序 ************/ void HeapSort(int *p, const int len) { assert(p != NULL); int i; int temp; for (i = 0; i < len; i++) { AdjustTop(p, len-i); //对剩余元素重新构建“大顶堆” temp = p[0]; p[0] = p[len-i-1]; p[len-i-1] = temp; } }
各种内部排序方法的比较:
1. 就平均时间性能而言,快速排序最佳,其所需时间最少,但快速排序在最坏情况下的性能不如堆排序;
2. 由于堆排序在最坏情况下的时间复杂度和平均时间复杂度差不多,所以待排序列中元素初始时的顺序对该排序算法没有太大影响。
2. 从方法的稳定性来看,所有时间复杂度为O(n^2)的简单排序法都是稳定的(包括插入排序、冒泡排序、选择排序)。一般来说,排序过程中的“比较”是在“相邻的两个记录关键字”间进行的排序方法都是稳定的。