基本排序算法汇总

把一些常用的基本排序算法汇总下来,以后忘记了回头看看,回忆的也快。代码实现中加入了自己的一些理解,把我看到的别人的实现中比较巧妙的放在这。

一种经过优化后的快速排序,来自《数据结构与算法分析》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;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值