代码改变世界

编程珠玑第十一章----排序

2012-07-27 13:15  javaspring  阅读(273)  评论(0编辑  收藏  举报

1、快速排序

     原理:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序。

void quickSort(int *a,int s,int t)   //数组首地址a,数组起始位置s(初始值为0),结束位置t(初始值为n-1)
{
	int i=s,j=t+1,temp,x=a[s];
	do
	{
		do 
		{
			i++;
		} while (a[i]<a[s]);  //找比第一个元素小的元素
		
		do 
		{
			j--;
		} while (a[s]<a[j]);  //找比第一个元素大的元素
		
		if (i<j)              //交换,将小的值放在左边,大的值放右边
		{
			temp=a[i];
			a[i]=a[j];
			a[j]=temp;
		}
		
	}while(i<j);             //划分子区间的条件
    
	
	a[s]=a[j];               //交换a[s]和a[j]的值,至此成功划分两个子区间
	a[j]=x;                  //a[j]的位置确定下来
	
	if(s<j-1)
		quickSort(a,s,j-1);  //递归排序左子区间
	if (j+1<t)
		quickSort(a,j+1,t);  //递归排序右子区间	
}

2、堆排序

     原理:首先建立最大根堆,将根元素和根堆中最后一个元素调换,这样最后一个元素就排好序了,然后将剩余的元素更新为最大根堆,按照前面的方法,直至根堆中只剩下最后一个元素,排序完成。

void initHeap(int *data,int n)                //初始化根堆
{
	int t;
	for (int i=(n-1)/2;i>=0;i--)
	{
		if (2*i+2<=n&&data[2*i+1]<data[2*i+2]) //左右子树比较,左子树存放较大值
		{
			t=data[2*i+1];
			data[2*i+1]=data[2*i+2];
			data[2*i+2]=t;
		}
		
		if (data[i]<data[2*i+1])               //左子树与父节点比较,父节点存放较大值
		{
			t=data[2*i+1];
			data[2*i+1]=data[i];
			data[i]=t;
		}
	}
}

void heapSort(int *data,int n)   //堆排序
{
	if (n<=0)
		return;           //递归终止条件
	
	initHeap(data,n);     //初始化根堆
	
	int t=data[0];        //根与最后一个数值替换
	data[0]=data[n];
	data[n]=t;             
	
	heapSort(data,--n);   //去掉最大值,继续递归	
}

3、归并排序

     原理:递归将待排序数组分割成n个部分,每部分只有1个元素,然后在递归将每2个部分元素进行排序,直至最后只剩下一个完整的部分,排序完成。

template<class T> void Merge(T a[],T TR[],int low,int mid,int high)  //归并排序 a[low]~a[mid] 、a[mid]~a[high]  
{
	int i = low;
	int j = mid + 1;
	int k = 0;
	while(i <= mid && j <= high)
	{
		if(a[i] < a[j]) 
			TR[k++] = a[i++];
		else
			TR[k++] = a[j++];
	}
	while(i <= mid) 
		TR[k++] = a[i++];
	
	while(j <= high) 
		TR[k++] = a[j++];
	
	for(i = low; i <= high; i++)        //将排序好的数据移回到原序列中
		a[i] = TR[i - low];
}

template<class T> void MSort(T a[],T temp[],int low,int high)
{
	if (low<high)                       //递归终止条件
	{
		int mid = (low + high) / 2;     //进行分裂
		MSort(a,temp,low,mid);          //将前一半继续分裂
		MSort(a,temp,mid + 1,high);     //将后一半继续分裂
		Merge(a,temp,low,mid,high);     //进行归并排序
	}
}

堆排序和快速排序都是不稳定的,而归并排序则是稳定的。

4、两个排好序的链表归并

pList mergeList(pList first,pList second)
{
	pList out,p1,p2,pLast;   //out为返回链表,p1指向first,p2指向second,pLast串联起两个链表
	
	//first链表为空
	if(first->next==NULL)             
		out=second;
	
	//second链表为空
	else if (second->next==NULL)
		out=first;
	
	//first和second都不为空
	else
	{
		p1=first->next;
		p2=second->next;
		
		//first链表为返回链表
		if (p1->data<=p2->data)
		{
			out=first;
			pLast=p1;               //pLast作用为串联这两个链表,指向返回链表的第一个节点
			p1=p1->next;
		} 
		
		//second链表为返回链表
		else
		{
			out=second;
			pLast=p2;
			p2=p2->next;
		}
		
		while(p1!=NULL&&p2!=NULL)
		{
			if (p1->data<=p2->data)
			{
				pLast->next=p1;  //pLast指向带返回的剩余节点
				pLast=p1;
				p1=p1->next;
			}
			else
			{
				pLast->next=p2;
				pLast=p2;
				p2=p2->next;
			}
		}
		
		if (p1!=NULL)
			pLast->next=p1;
		if(p2!=NULL)
			pLast->next=p2;
	}
	
	return out;
}

 5、计数排序

      计数排序算法针对待排序数组中的每个记录,扫描待排序的数组一趟,统计待排序数组中有多少个记录的值比该记录的值小。假设针对某一个记录,统计出的计数值为c,那么,这个记录在新的有序数组中的合适的存放位置即为c。

     计数排序一般适用于输入数据是由一个小范围的数据构成,而且是利用空间换取了时间

//计数排序,输入数组src,输出数组dest,数组长度n,数组中最大元素k
void CountSort(const int *src,int *dest,int n,int k)
{
	//c为计数数组,首先进行初始化
	int *c=new int[k+1];
	for(int i=0;i<k+1;i++)
		c[i]=0;

	//统计数组src中每个元素的个数
	for (i=0;i<n;i++)
	{
		c[src[i]]+=1;
	}

	//累计统计个数,即src中每个元素前面有多少个元素
	for (i=1;i<k+1;i++)
	{
		c[i]+=c[i-1];
	}

	//根据c中已知的src中每个元素前面有多少个元素,从大到小输出每个元素到dest数组
	int j=0;
	for (i=1; i<k+1;i++)
	{
		if (c[i]!=c[i-1])
		{
			j++;
			dest[j-1]=i;
		}
	}
}