排序算法

快速排序

快速排序是对冒泡排序的一种改进,它的基本思想是通过一趟排序将待排记录分割成独立的两部分,其中一部分的关键字比另一部分的关键字小,然后分别对这两部分进行快速排序,递归此过程,直到整个序列有序。

快速排序对包含n个数的输入数组,最坏情况运行时间较差,但是快速排序通常是用于排序的最佳的使用选择,它的平均性能相当好,期望为O(nlgn),隐含的常数因子很小。

就一趟排序而言,例如对数组a中的a[m..n]部分进行一趟快速排序,首先选择一个数组值,例如a[m]作为枢轴,然后1.从a[n]向左选择小于枢纽的数组值移动到a[m],2.从a[m+1]向右选择大于枢纽值移到步骤1移到左边的值的位置上,再从右边开始向左移动,重复1和2过程向中间靠拢,最后把枢纽值放在中间的某个位置上,完成一趟快速排序,代码如下:

//快速排序,对数组的a的low到high的元素a[low]-a[high]进行一次快速排序函数partition
int Partition(int a[],int low,int high)
{
	//交换数组a[low..high]的记录,使a[low..high]范围内枢纽记录左边的值<=枢纽记录,右边的值>=枢纽记录
	int pivotkey=a[low];           //用pivotkey暂存待排记录的第一个记录,作为枢轴记录

	//从a[low..high]的两端向中间扫描,low和high作为数组下标,依次向中间靠拢,当low=high时,结束依次快速排序
	while(low<high)                     
	{
		while(low<high && a[high]>=pivotkey) high--;    //查找比pivotkey小的数组值a[high],然后存在a[low]上
		a[low]=a[high];
		while(low<high && a[low]<=pivotkey) low++;      //查找比pivotkey大的数组值a[low],然后存在a[high]上
		a[high]=a[low];
	}//while

	//将枢纽值存放在a[low]上,完成一次快速排序
	a[low]=pivotkey;

	//返回枢纽的位置,为了下次对左右两部分进行快速排序
	return low;
}//Partition

递归形式的快速排序算法代码如下:

//快速排序函数QuickSort
void QuickSort(int a[],int low,int high)
{
	//对数组a[low..high]进行快速排序
	if(low<high)          
	{
		int av=Partition(a,low,high);          //av值记录调用Parrtiton函数返回的枢纽值的数组下标,然后将a[low..high]分为两部分递归快速排序
		QuickSort(a,low,av-1);                 //对a[low..av-1]进行快速排序
		QuickSort(a,av+1,high);                //对a[av+1..high]进行快速排序
	}
}//QuickSort

堆排序

堆排序属于选择排序,只需要一个记录大小的辅助空间,每个待排的记录仅占一个存储空间,运行时间复杂度为O(nlgn),是一种原地排序算法。

堆的定义如下,对于n个元素的序列{k1,k2,,kn},当满足k(i)<=k(2*i)且k(i)<=k(2*i+1)时称为小根堆,满足k(i)>=k(2*i)且k(i)>=k(2*i+1)时称为大根堆。如果将这个数组看成一个完全二叉树,则堆的含义表明,完全二叉树的所有非终端节点的值不大于(或者不小于)它的左、右孩子节点的值。如果上述数组是堆,则堆顶元素必为序列中n个元素的最小值(或者做大值)。

实现堆排序要解决两个问题:(1)如何由一个无序序列建成一个堆,(2)如何在输出堆顶元素后,调整剩余元素称为一个新的堆。

调整新堆思想(以大根堆为例):假设有一个二叉树对应的序列,根节点的左、右子树已经是大根堆,现在调整跟节点的值使调整后整个序列为一个大顶堆。令跟节点与孩子节点值较大的孩子节点比较,如果其值不小于孩子节点的值,则现在已经是大根堆;否则交换根节点和较大孩子节点的的位置,进行这一步后,新的根节点满足大根堆的定义,原来的根节点移到孩子节点的位置上,可能不满足大根堆的定义,此时以这个孩子节点作为新的根节点重复以上步骤。代码如下:

//堆排序,调整新堆函数HeapAdjust
void HeapAdjust(int a[],int s,int m)
{
	//数组a[0]作为建堆过程的辅助空间,a[1..m]作为一个完全二叉树,此函数实现的功能是在a[s]的左右子树均为堆的条件下,
	//建立a[s]以及其左右子树构成的完全二叉树也满足堆的定义
	a[0]=a[s];                       //辅助存储a[s]

	for(int j=2*s;j<=m;j*=2)
	{
		if(j<m && a[j]<a[j+1]) j++;   //选择左右孩子中较大的孩子
		if(a[j]<=a[0])                //如果较大的孩子的值不大于它,则已经查到a[0]的位置
			break;
		a[s]=a[j];                    //a[0]值较小,将孩子节点较大的存在a[s]上,并令s为此孩子节点
		s=j;
	}//for

	a[s]=a[0];                        //将a[0]即是原来的a[s]值放在查到到的适当的a[s]中,s可能已经改变
}//HeapAdjust
建堆思想: 含有元素 {k1,k2,,kn}的序列看成一个完全二叉树,则最后一个非终端节点是第 「n/2 」个元素,则从这个元素开始,执行HeapAdjust函数,向前逐步调整为堆。

堆排序:将堆顶元素(也就是序列的第一个)与未排序的最后一个元素交换位置(选择未排中最大的值排在后面),此时由于后面的值移到序列首位,执行HeapAdjust函数,调整新堆,反复此过程。

建堆、堆排序代码如下:

//堆排序实现函数HeapSort
void HeapSort(int a[],int m)
{
	//对数组进行堆排序
	for(int j=m/2;j>=1;j--)    //从a[m/2]到a[1],构建堆
		HeapAdjust(a,j,m);

	//把堆顶值与j下标值交换,使最大的值依次排在数组后面,然后重新建堆
	for(int j=m;j>1;--j)
	{
		a[0]=a[j];
		a[j]=a[1];
		a[1]=a[0];
		HeapAdjust(a,1,j-1);
	}//for
}//HeapSort

归并排序

归并排序是将两个或者两个以上的有序表组合成一个新的有序表。它是一种稳定的排序方法,过程需要额外的空间,归并思想容易理解,一次归并过程代码如下:

//归并排序,将数组的相邻的两个有序序列归并为一个有序序列Merge函数
void Merge(int a[],int b[],int i,int m,int n)
{
	//将有序的a[i..m]和有序的a[m+1..n]归并为有序的b[i..n]
	int k=i,s=m+1;

	//将a的两个有序序列归并为b的有序序列
	for(;s<=n && i<=m;k++)
	{
		if(a[i]<=a[s]) b[k]=a[i++];
		else b[k]=a[s++];
	}//for

	//当a的一个完全归并后,只需将另一个的剩余完全归并进b
	if(s>n)
		while(k<=n)
			b[k++]=a[i++];
	else
		while(k<=n)
			b[k++]=a[s++];
}//Merge
递归实现的2-路归并排序(此实现函数运行过程需要的辅助空间较大,可以参考其他效率高的函数实现)算法如下:

//递归实现的2-路归并函数MergeSort
void MergeSort(int a[],int b[],int s,int t)
{
	//将a[s..t]归并为b[s..t]
	if(s==t)
		b[s]=a[s];
	else
	{
		int c[N];
		int m=(s+t)/2;            //将a[s..t]平均分成a[s..m]和a[m+1..t]
		MergeSort(a,c,s,m);       //递归的将a[s..m]归并为c[s..m]
		MergeSort(a,c,m+1,t);     //递归的将a[m+1..t]归并为c[m+1..t]
		Merge(c,b,s,m,t);         //将c[s..m]和c[m+1..t]归并到b[s..t]
	}//else
}//MergeSort

各种排序方法的具体函数实现

#include <iostream>

using namespace std;

#define N 10           //定义待排数组的大小

//函数声明
void QuickSort(int a[],int low,int high);
void HeapSort(int a[],int m);
void MergeSort(int a[],int b[],int s,int t);

//main函数
int main(void)
{
	int a[N]={15,3,9,26,11,7,18,33,21,6};         //快速排序
	int a2[N+1]={0,15,3,9,26,11,7,18,33,21,6};     //堆排序
	int a3[N]={15,3,9,26,11,7,18,33,21,6};         //归并排序

	//对数组a进行快速排序输出排序结果
	cout<<"快速排序结果如下:"<<endl;
	QuickSort(a,0,9);
	for(int i=0;i<N;i++)
		cout<<a[i]<<" ";
	cout<<endl<<endl;

	//对数组a2进行堆排序输出排序结果
	cout<<"堆排序结果如下:"<<endl;
	HeapSort(a2,N);
	for(int i=1;i<=N;i++)
		cout<<a2[i]<<" ";
	cout<<endl<<endl;

	//对数组a3进行归并排序输出排序结果
	cout<<"归并排序结果如下:"<<endl;
	int a4[N];              //排序后
	MergeSort(a3,a4,0,9);
	for(int i=0;i<N;i++)
		cout<<a4[i]<<" ";
	cout<<endl<<endl;

	system("pause");
	return 0;
}

//*******************************************************
//快速排序,对数组的a的low到high的元素a[low]-a[high]进行一次快速排序函数partition
int Partition(int a[],int low,int high)
{
	//交换数组a[low..high]的记录,使a[low..high]范围内枢纽记录左边的值<=枢纽记录,右边的值>=枢纽记录
	int pivotkey=a[low];           //用pivotkey暂存待排记录的第一个记录,作为枢轴记录

	//从a[low..high]的两端向中间扫描,low和high作为数组下标,依次向中间靠拢,当low=high时,结束依次快速排序
	while(low<high)                     
	{
		while(low<high && a[high]>=pivotkey) high--;    //查找比pivotkey小的数组值a[high],然后存在a[low]上
		a[low]=a[high];
		while(low<high && a[low]<=pivotkey) low++;      //查找比pivotkey大的数组值a[low],然后存在a[high]上
		a[high]=a[low];
	}//while

	//将枢纽值存放在a[low]上,完成一次快速排序
	a[low]=pivotkey;

	//返回枢纽的位置,为了下次对左右两部分进行快速排序
	return low;
}//Partition

//快速排序函数QuickSort
void QuickSort(int a[],int low,int high)
{
	//对数组a[low..high]进行快速排序
	if(low<high)          
	{
		int av=Partition(a,low,high);          //av值记录调用Parrtiton函数返回的枢纽值的数组下标,然后将a[low..high]分为两部分递归快速排序
		QuickSort(a,low,av-1);                 //对a[low..av-1]进行快速排序
		QuickSort(a,av+1,high);                //对a[av+1..high]进行快速排序
	}
}//QuickSort


//***********************************************************
//堆排序,调整新堆函数HeapAdjust
void HeapAdjust(int a[],int s,int m)
{
	//数组a[0]作为建堆过程的辅助空间,a[1..m]作为一个完全二叉树,此函数实现的功能是在a[s]的左右子树均为堆的条件下,
	//建立a[s]以及其左右子树构成的完全二叉树也满足堆的定义
	a[0]=a[s];                       //辅助存储a[s]

	for(int j=2*s;j<=m;j*=2)
	{
		if(j<m && a[j]<a[j+1]) j++;   //选择左右孩子中较大的孩子
		if(a[j]<=a[0])                //如果较大的孩子的值不大于它,则已经查到a[0]的位置
			break;
		a[s]=a[j];                    //a[0]值较小,将孩子节点较大的存在a[s]上,并令s为此孩子节点
		s=j;
	}//for

	a[s]=a[0];                        //将a[0]即是原来的a[s]值放在查到到的适当的a[s]中,s可能已经改变
}//HeapAdjust

//堆排序实现函数HeapSort
void HeapSort(int a[],int m)
{
	//对数组进行堆排序
	for(int j=m/2;j>=1;j--)    //从a[m/2]到a[1],构建堆
		HeapAdjust(a,j,m);

	//把堆顶值与j下标值交换,使最大的值依次排在数组后面,然后重新建堆
	for(int j=m;j>1;--j)
	{
		a[0]=a[j];
		a[j]=a[1];
		a[1]=a[0];
		HeapAdjust(a,1,j-1);
	}//for
}//HeapSort

//*************************************************************************
//2-路归并排序,将数组的相邻的两个有序序列归并为一个有序序列Merge函数
void Merge(int a[],int b[],int i,int m,int n)
{
	//将有序的a[i..m]和有序的a[m+1..n]归并为有序的b[i..n]
	int k=i,s=m+1;

	//将a的两个有序序列归并为b的有序序列
	for(;s<=n && i<=m;k++)
	{
		if(a[i]<=a[s]) b[k]=a[i++];
		else b[k]=a[s++];
	}//for

	//当a的一个完全归并后,只需将另一个的剩余完全归并进b
	if(s>n)
		while(k<=n)
			b[k++]=a[i++];
	else
		while(k<=n)
			b[k++]=a[s++];
}//Merge

//递归实现的2-路归并函数MergeSort
void MergeSort(int a[],int b[],int s,int t)
{
	//将a[s..t]归并为b[s..t]
	if(s==t)
		b[s]=a[s];
	else
	{
		int c[N];
		int m=(s+t)/2;            //将a[s..t]平均分成a[s..m]和a[m+1..t]
		MergeSort(a,c,s,m);       //递归的将a[s..m]归并为c[s..m]
		MergeSort(a,c,m+1,t);     //递归的将a[m+1..t]归并为c[m+1..t]
		Merge(c,b,s,m,t);         //将c[s..m]和c[m+1..t]归并到b[s..t]
	}//else
}//MergeSort

运行结果显示



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值