把一些常用的基本排序算法汇总下来,以后忘记了回头看看,回忆的也快。代码实现中加入了自己的一些理解,把我看到的别人的实现中比较巧妙的放在这。
一种经过优化后的快速排序,来自《数据结构与算法分析》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;
}
}
}