把一些常用的基本排序算法汇总下来,以后忘记了回头看看,回忆的也快。代码实现中加入了自己的一些理解,把我看到的别人的实现中比较巧妙的放在这。
一种经过优化后的快速排序,来自《数据结构与算法分析》weiss,是本不错的书:
快速排序:
void Insertsort(int A[], int len) { int tmp; int i, j; for(i=1; i<len; i++) { tmp=A[i]; for(j=i; j>0 && tmp < A[j-1]; j--) A[j]=A[j-1]; A[j]=tmp; } } void Swap(int *lhs, int *rhs) { int tmp=*lhs; *lhs = *rhs; *rhs = tmp; } int median(int array[], int len) { int mid = len/2; int tmp; if(array[0]>array[mid]) Swap(&array[0], &array[mid]); if( array[ 0 ] > array[ len-1 ]) Swap(&array[0], &array[ len-1 ]); if(array[mid]>array[len-1]) Swap(&array[mid], &array[len-1]); Swap(&array[mid], &array[len-2]); return array[len-2]; } void Qsort(int A[], int low, int high) { int pivot=median(A, high-low+1); int i=low, j=high-1; if( i + 10 <= j ) { for(;;){ while( A[ ++i ] < pivot ){} while( A[ --j ] > pivot ){} if(i < j) Swap( &A[i], &A[j] ); else break; } Swap(&A[i], &A[high-1]); Qsort(A, low, i-1); Qsort(A, i+1, high); }else{ Insertsort(A, high-low+1); } }
基数排序,原理不提了。基数排序又分为LSD(Least Significant Digital)和MSD(Most Significant Digit),代表从最低位开始,还是最高位开始。LSD要简单些,MSD一般采用递归的形式。这里是对正整数进行基数排序,其它变形如对ascii字符串排序,则统计数组长度为128.下面分别给出代码。看懂了LSD的,MSD就容易明白了。
//返回整数x第d位的数字,x的最低位为0 int getdigit(int x, int d) { return (x / (int)pow(10, d)) % 10; } //arr[]包含要排序的整数数组,digits:整数数组中最大整数位数 void lsdradix_sort(int arr[], int len, int digits) { const int radix = 10; int count[radix], i, j; int *bucket = new int[(len)*sizeof(int)]; //开辟桶空间 for (int k = 0; k < digits; ++k) { for (i = 0; i < radix; i++) { count[i] = 0; } //统计各个桶槽中所盛数据个数,数字字符为0-9,所以一共只能有10个桶槽 //下标正好对应数字字符 for (i = 0; i < len; i++) { count[getdigit(arr[i], k)]++; } //数字字符i肯定只能排在count[i-1]个i-1的后面,虽然一次排序某个数的索引不能确定,但只能在count[i-1]+1和count[i]之间 for (i = 1; i < radix; i++) { count[i] = count[i] + count[i - 1]; } //因为count的值是按字符字典序从小到大累加的,放入某个桶中按右边界来索引,进行--操作,count[j]的值正好对应在桶中的索引,很巧妙。 //反之如果想从左往右,下面放入桶中应该按左边界来索引,但还需要另外一个变量保存当前字符j在count[j-1]+1到count[j]之间的具体位置,每个字符都要记录,比较麻烦。 //从右往左计算并不像网上说的是为了保证稳定性。保证稳定性在于下面关系的匹配: //从右往左时,桶索引要从count[j]-->count[j-1]+1递减; //从左往右时,桶索引要从count[j-1]+1-->count[j]递增. for (i = len-1;i >= 0; --i) { j = getdigit(arr[i], k); //求出关键码的第k位的数字, 例如:576的第3位是5 bucket[--count[j]] = arr[i]; //放入对应的桶中,count[j]是第j个桶的右边界索引,对应桶的装入数据索引减一(桶索引从0开始,所以先减) } for (i = 0; i < len; ++i) { arr[i] = bucket[i]; } } delete []bucket; }
//返回整数x第d位的数字,x的最低位为1 int getdigit(int x, int d) { return (x / (int)pow(10, d-1)) % 10; }//arr[]包含要排序的整数数组,digits:整数数组中最大整数位数 void msdradix_sort(int arr[], int begin, int end, int d) { const int radix = 10; int count[radix], i, j; for (i = 0; i <= radix; ++i) { count[i] = 0; } int *bucket = (int *)malloc((end - begin + 1) * sizeof(int)); //统计各桶需要装的元素的个数 for (i = begin;i <= end; ++i) { count[getdigit(arr[i], d)]++; } //求出桶的边界索引,count[i]值为第i个桶的右边界索引+1 for (i = 1; i < radix; ++i) { count[i] = count[i] + count[i - 1]; } for (i = end;i >= begin; --i) { j = getdigit(arr[i], d); //求出关键码的第d位的数字, 例如:576的第3位是5 bucket[--count[j]] = arr[i]; } for (i = begin, j = 0;i <= end; ++i, ++j) { arr[i] = bucket[j]; } free(bucket); //对各桶中数据进行再排序 for (i = 1;i < radix; i++) { int p1 = begin + count[i-1]; //第i个桶的左边界 int p2 = begin + count[i] - 1; if (p1 < p2 && d > 1) { msdradix_sort(arr, p1, p2, d - 1); //对第i个桶递归调用,进行基数排序,数位降1 } } }
堆排序:
/*heap sort,升序有序,所以用大顶堆*/ #define Left(x) (2*(x)) void percdown(int A[], int i, int len) { int child; for(; Left(i)<=len; i = child) { //下面代码主要意思是对每个非叶结点,看它是否比孩子结点小,小则进行向下渗透操作 child=Left(i); if(child != len && A[ child ] < A[ child+1 ]) //这里判断一下child != len,对于最后一个非叶结点,不相等说明还有右孩子。 child++; if(A[i] < A[child]) { //swap,没有用swap函数调用,要快些 A[i] = A[i] + A[child]; A[child] = A[i] - A[child]; A[i] = A[i] - A[child]; } else break; } } /*要排序的元素是从数组A下标为1处开始的,切记。A[0]用来存放需排序元素个数*/ void Headsort(int A[]) { for(int i=A[0]/2; i>=1; --i) percdown(A, i, A[0]); for(int i=A[0]; i>=2; --i) //结束条件为下标2,这样最后只剩下一个元素,肯定是最小的 { //Swap(&A[i], &A[1]); A[i] = A[i] + A[1]; A[1] = A[i] - A[1]; A[i] = A[i] - A[1]; percdown(A, 1, i-1); } }
希尔排序。 希尔排序本质是多路插入排序,下面两种实现技巧上有一点小差别,第一种是我自己想的,第二种是书上的。
void Shellsort1(int a[], int n){ int i,j,k; for(int gap=n/2; gap>0; gap /= 2){ for( i=0; i<gap; i++ ){ for( j=gap+i; j<n; j += gap ){ int tmp = a[j]; for( k=j; k>i && tmp<a[k-gap]; k -= gap ) a[k]=a[k-gap]; a[k]=tmp; } } } } void Shellsort2(int a[], int n){ int gap, i, j; int tmp; for( gap=n/2; gap>0; gap /=2){ for(i=gap; i<n; i++){ tmp = a[i]; for(j=i; j>=gap && a[j-gap]>tmp; j -= gap) a[j] = a[j-gap]; a[j]=tmp; } } }