排序算法
选择排序
1.直接选择排序
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 算法思想:进行N趟排序,每一趟都从未排序序列中选择一个最大或最小的值放入排序序列中
- 稳定性:不稳定
template<class KeyType> int min_key(KeyType A[], int low, int high) //从数据集合中选择最小的关键字,并返回数组下标 { int min_pos = low; for(int pos=low+1; pos<high; pos++) { if (A[pos]<A[min_pos]) { min_pos = pos; } } return min_pos; } template<class KeyType> void selection_sort(KeyType A[], int size) { int position; for (int i=0; i<size-1; i++) // 最后一个不用选了 { position = min_key<KeyType>(A, i, size); //从数据集合中寻找最小的数据 if (position!=i) { swap<KeyType>(A[i], A[position]); //交换两个数据 } } }
2.堆排序
- 时间复杂度:O(N*logN)
- 空间复杂度:O(1)
- 算法思想:建立一个最大堆或最小堆(堆:以二叉树为数据模型,根结点的数据大于子结点的数据叫做大根堆,反之即为小根堆),每次从堆顶取出一个元素放入目标数组中
- 稳定性:不稳定
template<class KeyType> void insert_heap(KeyType A[], KeyType current, int low, int high) //插入元素到堆中 { int large = 2*low + 1; //计算左边孩子结点的下标 while(large<high) { if (large+1<high && A[large]<A[large+1])//比较两个孩子结点的元素大小 { large++; } if (current<A[large]) //将插入的元素和较大的孩子结点的元素进行比较 { A[low] = A[large]; low = large; large = 2*low + 1; } else { break; } } A[low] = current; } template<class KeyType> void build_heap(KeyType A[], int size) //将一棵完全二叉树建立成大根堆 { for (int low=size/2-1; low>=0; low--) { insert_heap(A, A[low], low, size); } } template<class KeyType> void heap_sort(KeyType A[], int size) { build_heap(A, size);//建立大根堆 int current; for (int i=size-1; i>0; i--) { current = A[i];//叶子结点的值 A[i] = A[0]; //将根结点的值放到叶子结点处 insert_heap(A, current, 0, i);//将叶子结点的值重新插入到堆中 } }
插入排序
1.直接插入排序
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 算法思想:遍历数组,将当前元素插入到已排序序列中
- 算法特点:适合于大部分数据为有序状态的数组排序
- 稳定性:稳定
template<class KeyType> void insertion_sort(KeyType A[], int size) { int first_unsorted; //第一个待排序数据位置 KeyType current; //第一个待排序数据 int high, low; for (first_unsorted=1; first_unsorted<size; first_unsorted++) { if (A[first_unsorted]<A[first_unsorted-1]) { /*current = A[first_unsorted]; position = first_unsorted; do { A[position] = A[position-1]; position--; } while (position>0&¤t<A[position-1]); A[position] = current;*/ //采用二分插入排序,减少比较次数 current = A[first_unsorted]; low = 0; high = first_unsorted-1; while(low<high) //寻找插入位置 { int mid = low+(high-low)/2; if (A[mid]<=current) { low = mid+1; } else { high = mid-1; } } //移动元素 for (int i=first_unsorted; i>low; i--) { A[i] = A[i-1]; } A[low] = current; } } }
2.希尔排序
- 时间复杂度:
- 空间复杂度:O(1)
- 算法思想:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高
- 算法关键点:如何选择初始增量,以及如何缩减增量
- 稳定性:稳定
template<class KeyType> void shell_sort(KeyType A[], int size) { int increment = size; int first_unsorted, position; KeyType current; do { increment = increment/3 + 1; //计算增量,算法的关键点 for (int i=0; i<increment; i++) //根据增量分割待排序的元素 { for (first_unsorted=i+increment; first_unsorted<size; first_unsorted+=increment) { if (A[first_unsorted]<A[first_unsorted-increment]) //在内部用直接插入排序算法进行排序 { position = first_unsorted; current = A[first_unsorted]; do { A[position] = A[position-increment]; position -= increment; } while ((position-increment>=0)&&(current<A[position-increment])); A[position] = current; } } } } while (increment>1); }
交换排序
1.快速排序
- 时间复杂度:平均情况O(NlogN),最差情况(N^2)
- 空间复杂度:O(1)
- 算法思想:选择一个枢纽元,数组中小于枢纽元的数据放一边,大于枢纽元的数据放另外一边。不断递归的分割直至数组大小为1
- 稳定性:不稳定
template<class KeyType> KeyType median(KeyType A[], int low, int high) { int center = low + (high-low)/2; // A[low] <= A[center] <= A[high] if (A[center]>A[high]) Swap(A[center], A[high]); if (A[low]>A[center]) { Swap(A[low], A[center]); if (A[center]>A[high]) Swap(A[center], A[high]); } Swap(A[center], A[high-1]); return A[high-1]; } template<class KeyType> int partion(KeyType A[], int low, int high) { KeyType pivot = median(A, low, high); // 三数中值法取枢纽元 int left = low; int right = high-1; while (true) { while(A[++left]<pivot); // 从左往右找大于pivot的元素 while(A[--right]>pivot); // 从右往左找小于pivot的元素 if (left<right) Swap(A[left], A[right]); else break; } Swap(A[left], A[high-1]); return left; } template<class KeyType> void recursive_quick_sort(KeyType A[], int low, int high) { while (low+1<high) { int p = partion(A, low, high); recursive_quick_sort<KeyType>(A, low, p-1); low = p+1; } } template<class KeyType> void quick_sort(KeyType A[], int size) { recursive_quick_sort<KeyType>(A, 0, size-1); insertion_sort(A, size); }
2.冒泡排序
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 算法思想:进行N趟排序,每一趟从底部将最大的元素交换到顶部
- 稳定性:稳定
template<class KeyType> void bubble_sort(KeyType A[], int size) { bool flag = true; //标记每一趟是否交换过,决定是否提前结束循环 for (int i=1; i<size&&flag; i++) { flag = false; //每一趟的开始,设置交换标记为false for (int j=0; j<size-i; j++) { if (A[j]>A[j+1]) { swap<KeyType>(A[j], A[j+1]); flag = true; //设置交换标记为true } } } }
分配排序
1.基数排序
- 时间复杂度:O(MN)(M为最大位数)
- 空间复杂度:O(N)
- 算法思想:直接看代码
- 算法局限性:1.数据只能是非负的整数;2.得知道数据的最大位数(十进制)
- 稳定性:稳定
int maxBit(int A[], int size)//用来求数组中元素的最大位数 { int bit = 1; int num = 10; for (int i=0; i<size; ++i) { while(A[i]/num>0) { ++bit; num *= 10; } } return bit; } void radix_sort(int A[], int size) { int bit = maxBit(A, size);//计算数组中元素的最大位数 int count[10];//计数器 int * temp = new int[size];//辅助数组 int i, j, k; int radix = 1; for (i=1; i<=bit; i++)//进行bit趟排序 { for(j=0; j<10; ++j) { count[j] = 0; } for (j=0; j<size; ++j) //统计每个桶中的记录数 { k = A[j] / radix % 10; //计算所在位上的数字 ++count[k]; } for (j=1; j<10; ++j) { count[j] += count[j-1]; } for (j=size-1; j>=0; --j)// 将A中的数据存到temp中,从后往前,保持稳定性 { k = A[j] / radix % 10; //计算所在位上的数字 --count[k]; temp[count[k]] = A[j]; } for (j=0; j<size; ++j)//将temp中的数据复制到A中 { A[j] = temp[j]; } radix *= 10; } delete [] temp; }
2.位图排序
- 时间复杂度:O(N)
- 空间复杂度:O(MaxValue/32)
- 算法思想:将每个数据映射到某个数组元素的某一位
- 算法局限性:1.数据只能是非负的整数;2.数据不能重复;3.须知道最大的数据
- 算法特点:在数据集合满足条件的情况下,适合查询数据有没有存在
- 稳定性:数据没有重复,自然是稳定的
//sort the array by bitmap, it demand all elements of src can't be repeated. const int WORD = 32; const int SHIFT = 5; //left shift 5 bits equal to mutiply 32 const int MASK = 0x1f;//31,M%N: if N%2=0, M%N = M&(N-1) const int MAX_VALUE = 10000000; //The maximum value of all elements of src. int bitmap[MAX_VALUE/WORD+1]; //The array of bitmap. // set the bit // i right shift 5 bit to calculate the index // 1<<(i&0x1f) to calculate the bit void set(int i) { bitmap[i>>SHIFT] |= (1<<(i&MASK)); } //clear the bit void clear(int i) { bitmap[i>>SHIFT] &= ~(1<<(i&MASK)); } //return the result of the bit int test(int i) { return (bitmap[i>>SHIFT] & (1<<(i&MASK))); } void bitmap_sort(int src[], int size) { int i; for (i=0; i<MAX_VALUE; ++i) { clear(i); } for (i=0; i<size; ++i) { set(src[i]); } int j=0; for (i=0; i<MAX_VALUE; ++i) { if (test(i)) { src[j++] = i; } } }
3.计数排序
- 时间复杂度:O(N)
- 空间复杂度:O(MaxValue)
- 算法思想:将每个数据映射到某个数组元素的某一位
- 算法局限性:数据只能是整数
- 算法特点:在数据集合满足条件的情况下,适合数据集合跨度(Max-Min)不是很大的情况
- 稳定性:稳定
void count_sort(int A[], size_t size) { if (size>0) { int max, min; max = min = A[0]; size_t i; for(i=1; i<size; ++i) { if (A[i]>max) max = A[i]; else if (A[i]<min) min = A[i]; } max = max - min + 1; size_t* Count = new size_t[max]; // min as offset value memset(Count, 0, max*sizeof(size_t)); // initialize for (i=0; i<size; ++i) // count ++Count[A[i]-min]; size_t pos = -1; // index of A[] // sort for (i=0; i<max; ++i) { while (Count[i]>0) { A[++pos] = i + min; --Count[i]; } } delete [] Count; } }
归并排序
1.归并排序
- 时间复杂度:O(N*logN)
- 空间复杂度:O(N)
- 算法思想:分而治之。分:把数组想像成二叉数的根结点,先将该根结点向下逐层分裂成叶结点只含一个元素,这颗树的高度即为logN;治:再从叶结点向上逐层合并,并且在合并的过程中同时排序
- 稳定性:稳定
template<class KeyType> void two_merge(KeyType dest[], KeyType src[], int low, int m, int high) //归并两个有序序列 { int i = low; int j = m; while(i<m&&j<high) //两个序列中都存在未归并元素 { if (src[i]<=src[j]) { dest[low++] = src[i++]; } else { dest[low++] = src[j++]; } } while(i<m) //第一个有序序列中存在未归并元素 { dest[low++] = src[i++]; } while(j<high) //第二个有序序列中存在未归并元素 { dest[low++] = src[j++]; } } template<class KeyType> void merge_pass(KeyType dest[], KeyType src[], int len, int size) { int p = 0; //p指向每一对待归并的第一个元素下标 while (p+2*len<size)//两两归并长度均为len的有序子序列 { two_merge<KeyType>(dest, src, p, p+len, p+2*len); p += 2*len; } if (p+len<size) //归并最后两个长度不等的有序子序列 { two_merge<KeyType>(dest, src, p, p+len, size); } else { for (int i=p; i<size; i++) { dest[i] = src[i]; //把剩下的最后一个有序子序列复制到swap中 } } } template<class KeyType> void merge_sort(KeyType A[], int size) { KeyType* temp = new KeyType[size];//构造一个临时数组 int len = 1; //有序序列的长度 while(len<size) { merge_pass<KeyType>(temp, A, len, size); len = len*2; merge_pass<KeyType>(A, temp, len, size); len = len*2; } delete [] temp; }