排序算法学习笔记

排序是最基本最常见的算法,你在编程的过程中会发现许多算法都是基于排序算法的转变。
排序算法的分类如下图所示,我们这里仅仅介绍除基数排序外的内部排序算法,最简单最常见的7种排序算法。


注*
排序算法的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,则称这种排序算法是稳定的;否则称为不稳定的。
------------------------------------------------------

选择排序(Simple Selection Sort)

我认为最直观的排序方法。
基本思想
在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
排序实例
初始状态         [49 38 65 97 76 13 27 49]
第一趟排序后 13 [49 65 97 76 38 27 49]
第二趟排序后 13 27 [65 97 76 49 38 49]
第三趟排序后 13 27 38 [97 76 49 65 49]
第四趟排序后 13 27 38 49 [76 97 65 49]
第五趟排序后 13 27 38 49 49 [97 65 76]
第六趟排序后 13 27 38 49 49 65 [97 76]
第七趟排序后 13 27 38 49 49 65 76 [97]
C++实现
template <typename T>
void sim_sel_sort(std::vector<T>& a)
{
	for(int i=0; i<a.size()-1; ++i){
		int min=i; //设a[i]是其后面最小的数
		for(int j=i+1; j<a.size(); ++j){ //找出a[i]及其后面最小的数
			if(a[j]<a[min])
				min=j;
		}
		T tmp=a[i]; //交换a[i]和最小的数
		a[i]=a[min];
		a[min]=tmp;
	}
}
--------------------------------------------------------

冒泡排序(Bubble Sort)

这是C程课中教的第二种排序方法。
基本思想
对当前还未排好序的范围内的全部数,自上而下地对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。第一趟冒泡把最大数移到最上面,第二趟把第二大的数移到第二的位置……以此类推直到没有任何一对数字需要比较。直观来看,气泡由大到小依次上浮。
排序实例

C++实现
template <typename T>
void bubble_sort(std::vector<T>& a)
{
	for(int i=a.size(); i>0; i--){ //需要排序的部分上限i
		for(int j=0; j<i-1; j++){ //需要排序的部分[0,i]
			if(a[j]>a[j+1]){ //后面比前面小,交换
				T tmp=a[j];
				a[j]=a[j+1];
				a[j+1]=tmp;
			}
		}
	}
}
-----------------------------------------

插入排序(Straight Insertion Sort)

基本思想
在第p趟,将位置p上的元素向左移动至它前面的正确位置上,这样就保证了位置0至位置p的元素已经排好序了。当最后一趟进行完毕,所有的元素就排好序了。
排序实例
C++实现
template <typename T>
void str_ins_sort(std::vector<T>& a)
{
	for(int i=1; i<a.size(); i++){ //a[i]需要移到正确的位置去
		int ins=i;
		T tmp=a[i];
		for(int j=i; j>0; j--){
			if(a[j-1]>tmp){ 
				a[j]=a[j-1]; //比a[i]大的都后移一位
				ins=j-1; //找出a[i]应该插入的位置
			}
			else
				break;
		}
		a[ins]=tmp; //插入正确位置
	}
}
---------------------------------

希尔排序(Shell Sort)

希尔排序是就像分组的直接插入排序,又称缩小增量排序。
基本思想
希尔排序需要一个增量序列d1, d2, …… dt,其中dt=1, d(k)>d(k+1)。所有距离为d1的倍数的记录放在同一个组中,先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1,即所有记录放在同一组中进行直接插入排序为止。dt=1保证能够实现排序正确。
排序实例
C++实现
template <typename T>
void shell_sort(std::vector<T>& a)
{
	for(int gap=a.size()/2; gap>0; gap/=2){ //增量序列
		for(int i=gap; i<a.size(); i++){ //a[i]需要移到该组正确的位置去
			int ins=i;
			T tmp=a[i];
			for(int j=i; j>=gap; j-=gap){ //a[i]只与该组的数相比
				if(a[j-gap]>tmp){
					a[j]=a[j-gap]; //比a[i]大的都后移gap位
					ins=j-gap; //找出a[i]应该插入的位置
				}
				else
					break;
			}
			a[ins]=tmp; //插入正确位置
		}
	}
}


----------------------------------

堆排序(Heap Sort)

堆排序的思想来自于二叉堆,以下讨论假设利用大顶堆。
基本思想
将待排序列造成一个大顶堆,此时整个序列的最大值就是堆顶的根结点,将它与堆数组末尾元素交换,然后将剩余的n-1个元素重新构造成一个大顶堆。再将新大顶堆的根结点和末尾元素交换,如此反复进行,就得到一个左小右大的有序序列。
排序实例
原序列
大顶堆
排序过程中
排序后
C++实现
//无序序列调整为一个大顶堆,a[0]~a[n-1]为堆,此处与二叉堆不同,二叉堆a[1]~a[n]
template <typename T>
void heap_adjust(std::vector<T>& a, int node, int n)
{
	for(int child=2*node+1; child<n; child=2*child+1){
		if(child!=n-1 && a[child]<a[child+1])
			child++;
		if(a[node]<a[child]){
			std::swap(a[node],a[child]);
			node=child;
		}
		else
			break;
	}
}
//堆排序
template <typename T>
void heap_sort(std::vector<T>& a)
{
	//无序序列调成大顶堆堆
	for(int i=a.size()/2; i>=0; i--)
		heap_adjust(a, i, a.size());
	//根结点与末尾元素交换,DeleteMax,并把剩下的元素重新调成大顶堆
	for(int j=a.size()-1; j>0; j--){
		std::swap(a[0],a[j]); //根结点与末尾元素交换,DeleteMax
		heap_adjust(a, 0, j); //把剩下的元素重新调成大顶堆
	}
}
---------------------------------------

归并排序(Merge Sort)

归并排序是递归思想的范例。
基本思想
假设初始序列有n个元素,可以看成n个有序的子序列,每个子序列长度为1,然后两两合并,得到[n/2]个长度为2(或1)的有序子序列;再两两归并,……,如此重复,直至得到一个长度为n的有序序列位置。
排序实例
C++实现
//归并
template <typename T>
void merge(std::vector<T>& a, std::vector<T>& tmpArray, int LeftBegin, int RightBegin, int RightEnd)
{
	int LeftEnd=RightBegin-1;
	int LeftPos=LeftBegin;
	int RightPos=RightBegin;
	int tmpPos=LeftBegin;
	//把a中元素有序地复制到tmpArray
	while(LeftPos<=LeftEnd && RightPos<=RightEnd){
		if(a[LeftPos]<a[RightPos])
			tmpArray[tmpPos++]=a[LeftPos++];
		else
			tmpArray[tmpPos++]=a[RightPos++];
	}
	while(LeftPos<=LeftEnd)
		tmpArray[tmpPos++]=a[LeftPos++];
	while(RightPos<=RightEnd)
		tmpArray[tmpPos++]=a[RightPos++];
	//把tmpArray复制到a中对应位置
	int n=RightEnd-LeftBegin+1;
	for(int i=0; i<n; i++)
		a[LeftBegin+i]=tmpArray[LeftBegin+i];
}
//含有4个参数的归并排序函数
template <typename T>
void merge_sort_tmp(std::vector<T>& a, std::vector<T>& tmpArray, int begin, int end)
{
	if(begin<end){
		int center=(begin+end)/2;
		merge_sort_tmp(a, tmpArray, begin, center); //对左半边进行递归
		merge_sort_tmp(a, tmpArray, center+1, end); //对右半边进行递归
		merge(a, tmpArray, begin, center+1, end); //将左半边和右半边使用merge函数归并
	}
}
//只含有1个参数的归并排序
template <typename T>
void merge_sort(std::vector<T>& a)
{
	std::vector<T> tmpArray(a.size());
	merge_sort_tmp(a, tmpArray, 0, a.size()-1);
}
-----------------------------------------

快速排序(Quick Sort)

快速排序是在实践中最快的已知排序算法。
基本思想
取待排序列中的一个元素p,称为枢纽元pivot,将待排序列分为2个部分,一部分所有元素均≤p,另一部分所有元素均≥p。对分好的两部分继续进行上述操作,递归操作,至两部分元素数目很少(约为10),对齐使用插入排序。这样整个整个序列就排好序了。
排序实例
C++实现
//从3个数中找中值,并把min, mid, max放到a[left],a[right-1],a[right]上
template <typename T>
const T& median3(std::vector<T>& a, int left, int right)
{
	//3个数,比较一遍,大者往右,小者往左,结果为min, mid, max
	int center=(left+right)/2;
	if(a[center]<a[left])
		std::swap(a[center],a[left]);
	if(a[right]<a[left])
		std::swap(a[right],a[left]);
	if(a[right]<a[center])
		std::swap(a[right],a[center]);
	//把枢纽元放在right-1的位置上
	std::swap(a[center],a[right-1]);
	return a[right-1];
}
//含有3个参数的快速排序
template <typename T>
void quick_sort_tmp(std::vector<T>& a, int left, int right)
{
	if(right-left>=10){
		T pivot=median3(a, left, right);  //计算后枢纽元放在a[right-1]
		int i=left, j=right-1;
		while(i<j){
			while(a[i]<pivot) //i左移,移过比pivot小的a[i]
				i++;
			while(a[j]>pivot) //j左移,移过比pivot大的a[j]
				j++;
			//当i和j都停止时,i指向一个比pivot大的元素,j指向一个比pivot小的元素
			if(i<j) //如果i<j,a[i]和a[j]交换
				std::swap(a[i], a[j]);
			else //如果i和j已经交错,跳出循环
				break;
		}
		std::swap(a[i], a[right-1]); //把枢纽元a[right-1]放到正确的位置i处
		quick_sort_tmp(a, left, i-1);
		quick_sort_tmp(a, i+1, right);
	}
	else//对于小数组(N=10左右),插入排序比快速排序好些
		str_ins_sort(a);
}
//含有1个参数的快速排序
template <typename T>
void quick_sort(std::vector<T>& a)
{
	quick_sort_tmp(a, 0, a.size()-1);
}






posted @ 2014-08-24 16:02  东邪他爹  阅读(127)  评论(0编辑  收藏  举报