归并排序:将两个或两个以上的有序表组合一个新的有序表称为“归并”。先使每个子序列有序,再归并使子序列段有序,最后得到完全有序的序列。
算法思想:我们通常用递归实现,先把待排序区间[startindex,endindex]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[startindex,endindex]。
归并过程:比较a[i]和b[j]的大小,若a[i]≤b[j],则将第一个有序序列中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序序列中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序序列取完,然后再将另一个有序序列中剩余的元素复制到r中从下标k到下标endindex的单元。
如图所示:
上半部分为2分法使数据为有序序列,下半部分为归并有序序列操作,最后为完全有序。
示例代码如下:(测试代码自己写)
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
void Merge(int *arr, int *tmp, int startindex, int midindex, int endindex)//归并操作 { int k = startindex; int i = startindex; int j = midindex + 1; while(i != midindex + 1 && j != endindex + 1) { if(arr[i] > arr[j]) { tmp[k++] = arr[j++]; } else { tmp[k++] = arr[i++]; } }
while(i != midindex + 1) { tmp[k++] = arr[i++]; }
while(j != endindex + 1) { tmp[k++] = arr[j++]; }
for(int i = startindex; i <= endindex; i++) { arr[i] = tmp[i]; } }
void Guib(int *arr, int *tmp, int startindex, int endindex)//归并排序 { if(startindex < endindex) { int midindex = (startindex + endindex) / 2; Guib(arr, tmp, startindex, midindex); Guib(arr, tmp, midindex + 1, endindex); Merge(arr, tmp, startindex, midindex, endindex); } } |
注:测试只需在main函数中建立两个数组,调用Guib函数即可。
时间复杂度:一趟归并排序调用n/2h次算法merge将arr【1..n】中前后相邻且长度为h的有序段两两合并得到2h的有序段,并存放到tmp【1..n】中。整个归并排序需要log2^n趟。所以需要移动操作2*2h*(n/2h)*log2^n=2log2^n;比较操作是(n/2h)*h*log2^n=n/2log2^n;此算法时间复杂度为nlgn.
空间复杂度:O(1)。
稳定性:每次归并若元素相同,不改变前后顺序,所以为稳定的。
续:非递归实现代码
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
|
#include #include void MergeSort(int *arr, int *tmp, int length) { int leftmin = 0; int leftmax = 0; int rightmin = 0; int rightmax = 0; int k = 0; for(int i = 1; i < length; i *= 2) { for(int leftmin = 0; leftmin < length - i; leftmin = rightmax + 1) { leftmax = leftmin + i; rightmin = leftmax + 1; rightmax = rightmin + i - 1;
if(rightmax > length) { rightmax = length; }
while(leftmin < leftmax + 1 && rightmin < rightmax + 1) { tmp[k++] = arr[leftmin] > arr[rightmin] ? arr[rightmin++] : arr[leftmin++]; }
while(leftmin < leftmax + 1) { tmp[k++] = arr[leftmin]; }
while(rightmin < rightmax + 1) { tmp[k++] = arr[rightmin]; }
for(int j = leftmin; j <= rightmax; j++) { arr[j] = tmp[j]; } } }
} #define N 10 int main() { int a[N] = {3, 1, 4, 5, 7, 6, 0, 8, 7, 9}; int t[N]; MergeSort(a, t, N);
for(int i = 0; i < N; i++) { printf("%d", a[i]); } } |
基数排序:又称“桶排序”,通过键值的部分的资讯,将要排序的元素分配到某些“桶”中,以此达到排序的目的。
算法思想:从最次位的关键字K^(d-1)起进行排序。然后再对高一位的关键字K^(d-2)进行排序,依次重复,直至对K^0进行排序后成为一个有序序列。叫“最低位优先”,简称LSD法。
排序过程如图所示:
代码如下:
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
|
//数据最大值的位数 即排序躺数 (543)3 int FindMaxFigure(int *arr, int len) { int max = arr[0]; for(int i = 1; i < len; i++) { if(max < arr[i]) { max = arr[i]; } } int count = 0; do { max /= 10; count++; } while(max != 0); return count; } //数据的位上的数字 (45 0)1、(54 1)5 int FindNums(int num, int figure) { return num / pow(10.0, figure) % 10; } //根据数据每一位进行入桶和出桶操作 void Radix(int *arr, int len, int figures) { int tmp[10][N] = {}; int count[10] = {}; for(int i = 0; i < len; i++) { int num_figures = FindNums(arr[i], figures); tmp[num_figures][count[num_firures]] = arr[i]; count[num_figures]++; } int k = 0; for(int i = 0; i < 10; i++) { for(int j = 0; j < count[i]; j++) { arr[k++] = tmp[i][j]; } } } //基数排序(桶排序) void RadixSort(int *arr, int len) { int num = FindMaxFigure(arr, len); for(int i = 0; i < num; i++) { Radix(arr, len, i); } } |
注:测试只需在main函数中调用void RadixSort(int *arr, int len)就行。
时间复杂度: 可知排序躺数为count次,入桶和出桶操作为n,赋值给原始数组为r*n,r为基数。所以时间复杂度为 O(n(count+r))。
空间复杂度:辅助存储数组tmp、count,所以为O(r*n+r),r为基数。
稳定性:稳定。