前面介绍了选择排序、冒泡排序、插入排序、归并排序、堆排序、快速排序和桶排序,下面对这些排序算法进行总结。
实现
#include <iostream>
#include <stdlib.h>
using namespace std;
class Sort{
public:
//插入排序
static void insertionSort(int arr[],int length){
if(arr == NULL || length <= 1){
return;
}
int i = 1,j = 0;
for( ; i < length; i++){
for(j = i; arr[j] < arr[j - 1] && j > 0; j--){
swap(arr[j],arr[j - 1]);
}
}
}
//快速排序
static void quickSort(int arr[],int L,int R){
if( L >= R)
return;
int num = arr[rand() % (R - L + 1)];
//cout << num << endl;
int less = L - 1;
int more = R + 1;
int i = L;
while( i < R){
if(arr[i] > num){
swap(arr[i],arr[--more]);
}
else if( arr[i] < num) {
swap(arr[i++],arr[++less]);
}
else{
i++;
}
}
quickSort(arr,L, less);
quickSort(arr,more,R);
}
static void quickSort(int arr[],int length){
if(arr == NULL || length <= 1){
return;
}
quickSort(arr,0,length - 1);
}
//归并排序
static void merge(int arr[],int L,int mid,int R){
//cout << "L:" << L << " mid:" << mid << " R:" << R<< endl;
int * temp_arr = new int(R - L + 1);
int i = L,j = mid + 1,k = 0;
while(i <= mid && j <= R){
temp_arr[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
}
while(i <= mid){
temp_arr[k++] = arr[i++];
}
while(j <= R){
temp_arr[k++] = arr[j++];
}
for(i = L,k = 0; i <= R; i++){
arr[i] = temp_arr[k++];
}
}
static void mergeSort(int arr[],int L,int R){
if( L >= R){
return;
}
int mid = L + ((R - L) >> 1);//
//cout << "mid: " << mid << endl;
mergeSort(arr,L,mid);
//cout << "mid + 1: " << mid + 1 << endl;
mergeSort(arr,mid + 1,R);
merge(arr,L,mid,R);
}
static void mergeSort(int arr[],int length){
if(arr == NULL || length <= 1){
return;
}
mergeSort(arr,0,length - 1);
}
//堆排序
static void heapInsert(int arr[],int index){
// cout << ((index - 1) >> 1) << endl;
while(arr[index] > (arr[(index - 1) / 2])){
swap(arr[index],arr[(index-1)/ 2]);
index = ((index - 1) / 2);
}
}
static void heapify(int arr[],int index,int heapSize){
int L = index * 2 + 1;
while ( L < heapSize){
int largest = L + 1 < heapSize && arr[L + 1] > arr[L] ? L + 1 : L;
largest = arr[largest] > arr[index] ? largest :index;
if(largest == index)
break;
swap(arr[largest],arr[index]);
index = largest;
L = index * 2 + 1;
}
}
static void heapSort(int arr[],int length){
if(arr == NULL || length <= 1){
return;
}
int i = 0;
for( ; i < length; i++){
heapInsert(arr,i);
}
int heapSize = length;
swap(arr[0],arr[--heapSize]);
while(heapSize > 0) {
heapify(arr,0,heapSize);
swap(arr[0],arr[--heapSize]);
}
}
//计数排序
static void countSort(int arr[],int length){
if(arr == NULL || length <= 1){
return;
}
int max = INT_MIN;
cout << max << endl;
int i = 0;
for( ; i < length; i++){
max = arr[i] > max ? arr[i]: max;
}
//cout << "max: " << max << endl;
int* temp_arr = new int(max + 1);
for(i = 0; i <=max; i++)
temp_arr[i] = 0;
for(i = 0; i < length; i++){
temp_arr[arr[i]]++;
}
int j = 0;
for(i = 0; i <= max;i++){
while((temp_arr[i]--) > 0)
arr[j++] = i;
}
}
//基数排序
//得到数组arr中最大的数是几位数
static int getMaxBits(int arr[],int length){
int max = INT_MIN;
int i = 0;
for( ; i < length; i++){
max = arr[i] > max ? arr[i]: max;
}
for( i = 0; max != 0; i++){
max /= 10;
}
return i;
}
//得到数字num的d位数是多少(如13的个位数字(d = 1)是3)
static int getDigit(int num,int d){
int i = 0;
int divisor = 1;
for( ; i < d; i++){
divisor *= 10;
}
return num % divisor;
}
static void radixSort(int arr[],int begin,int end,int digit){
const int radix = 10;
int i = 0,j = 0;
int* bucket = new int(end - begin + 1);//准备与原数组数量相同的桶
int d = 1;
//最大数字的最高位为digit位,从个位遍历digit位
for( ; d <= digit; d++){
int count_arr[radix] = {0};
//从这里一直到193行的作用:arr数组中每个数字在d位是多少,为0则count[0]++,为1则count[1]++
//然后将前一个数累加到后一个数字上,即arr[1] = arr[1] + arr[0],arr[1] 表示在d位上0--1的数字有多少个
for(i = begin; i <= end; i++){
count_arr[getDigit(arr[i],d)]++;
}
for(i = 1; i < radix;i++){
count_arr[i] += count_arr[i - 1];
}
for(i = end;i >= begin;i--){
int j = getDigit(arr[i],d);
bucket[count_arr[j] - 1] = arr[i];
count_arr[j]--;
}
for(i = begin,j = 0; i <= end; i++,j++ ){
arr[i] = bucket[j];
//cout << arr[i] << " ";
}
}
// cout << endl;
}
static void radixSort(int arr[],int length){
if(arr == NULL || length <= 1){
return;
}
radixSort(arr,0,length - 1, getMaxBits(arr,length));
}
//for test
static void printfArray(int arr[],int length){
int i = 0;
for( ; i < length; i++)
cout << arr[i] << " ";
// cout << endl;
}
};
int main()
{
int arr[5] = {1,3,4,2,5};
int i = 0;
//Sort::insertionSort(arr,5);
//Sort::quickSort(arr,5);
//Sort::mergeSort(arr,5);
//Sort::heapSort(arr,5);
//Sort::countSort(arr,5);
Sort::radixSort(arr,5);
Sort::printfArray(arr,5);
return 0;
}
比较
几个算法的时间复杂度、空间复杂度与稳定性如下:
排序算法 | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
选择排序 | O(N^2) | O(1) | × |
冒泡排序 | O(N^2) | O(1) | √ |
插入排序 | O(N^2) | O(1) | √ |
归并排序 | O(N*logN) | O(N) | √ |
堆排序 | O(N*logN) | O(1) | × |
快速排序 | O(N*logN) | O(logN) | × |
所谓稳定性,就是同样值的个体之间,如果不会因为排序操作而改变相对次序,那么这个排序方法就具有稳定性。
除了表中列出的排序算法,一切基于桶排序思想下的排序都是稳定的。
在数据量较小时,建议选择插入排序的算法,因为时间复杂度为O(N^2)的排序算法的额为的常数时间的操作较少,在数据量较小时反而比下面的时间复杂度更低的算法排序更快。另外,选择排序与冒泡排序的常数时间的操作都是固定的,而插入排序的操作数量与数据的质量有关,在最好的情况下,如果数组本身就是有序的,那么插入排序的时间复杂度为O(N)。
工程上对排序的改进
C++中,sort函数的声明如下:
#include <algorithm>
template< class RandomIt >
void sort( RandomIt first, RandomIt last );
template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );
sort函数并不是简单的快速排序,除了对快速排序进行优化以外,它还会采用堆排序和插入排序。
当数据量比较大时采用快速排序,分段递归。一旦分段后的数据量小于某个阈值,为避免递归调用带来过大的额外负荷,便会改用插入排序。而如果递归层次过深,有出现最坏情况的倾向,还会改用堆排序。
优化后的快速排序:取整个数列的首、尾、中央三个地方的元素,以其中值作为分界值。分割的方法通常采用两个迭代器head
和tail
,head
从头端往尾端移动,tail
从尾端往头端移动,当head
遇到大于等于pivot
的元素就停下来,tail
遇到小于等于pivot
的元素也停下来,若head
迭代器仍然小于tail
迭代器,即两者没有交叉,则互换元素,然后继续进行相同的动作,向中间逼近,直到两个迭代器交叉,结束一次分割。
实现:
template <class _RandomAccessIter>
inline void sort(_RandomAccessIter __first, _RandomAccessIter __last) {
__STL_REQUIRES(_RandomAccessIter, _Mutable_RandomAccessIterator);
__STL_REQUIRES(typename iterator_traits<_RandomAccessIter>::value_type,
_LessThanComparable);
if (__first != __last) {
__introsort_loop(__first, __last,
__VALUE_TYPE(__first),
__lg(__last - __first) * 2);
__final_insertion_sort(__first, __last);
}
}
详细可以参考:https://www.cnblogs.com/fengcc/p/5256337.html