<C/C++算法> 八大经典排序算法的性能对比与总结

原创 2014年06月10日 20:05:01

一,各排序算法的思想及其稳定性

以下排序算法的排序结果若无特殊说明均为升序,swap函数的具体实现如下,每次的排序算法不再重复。(吐槽:多年后的今天来看,尼玛,都忘了,看不懂了!真佩服自己

void swap(int *a, int i, int j) //交换两个数  
{
        if(i==j)
           return;
	int tmp = a[i];
	a[i] = a[j];
	a[j] = tmp;
}

1,八大经典排序算法

(1)冒泡排序

依次比较相邻的数据,将小数据放在前,大数据放在后;即第一趟先比较第1个和第2个数,大数在后,小数在前,再比较第2个数与第3个数,大数在后,小数在前,以此类推则将最大的数"滚动"到最后一个位置;第二趟则将次大的数滚动到倒数第二个位置......第n-1(n为无序数据的个数)趟即能完成排序。
以下面5个无序的数据为例:
40 8 15 18 12 (文中仅细化了第一趟的比较过程)
第1趟: 8 15 18 12 40
第2趟: 8 15 12 18 40
第3趟: 8 12 15 18 40
第4趟: 8 12 15 18 40

void bubblesort(int *arr,int nsize)
{
	//排序,从i开始排,总是在[i+1,nsize]去寻找比当前元素小的,并且交换,使得当前位置的值总是其后面最小的。
	for (int i = 0; i < nsize; i++)
		for (int j = i + 1; j < nsize; j++)
			if (arr[i] > arr[j]) swap(arr, i,j);
}



(2)选择排序

以升序为例,比如在一个长度为N的无序数组中,
在第一趟遍历N个数据,找出其中最小的数值与第一个元素交换,
第二趟遍历剩下的N-1个数据,找出其中最小的数值与第二个元素交换......
第N-1趟遍历剩下的2个数据,找出其中最小的数值与第N-1个元素交换,
至此选择排序完成。
以下面5个无序的数据为例:
56 12 80 91 20(文中仅细化了第一趟的选择过程)
第1趟:12 56 80 91 20
第2趟:12 20 80 91 56
第3趟:12 20 56 91 80
第4趟:12 20 56 80 91

核心函数:

void selectsort(int *arr, int nsize)
{
	for (int i = 0; i < nsize; i++)
	{
		//arr[i+1....nsize-1]中找到当前最小值及其位置(准备与当前a[i]调换)  
		int min = arr[i];
		int minpos = i;
		for (int j = i + 1; j < nsize; j++)
		{
			if (arr[j] < min)
			{
				min = arr[j]; //当前最小值  
				minpos = j;//记录当前最小值的位置  
			}
		}
		swap(arr, i, minpos);//交换最小值位置,a[0....i]已经有序
	}
}


tip:

冒泡法和选择排序很像,两者区别在于:

冒泡排序是每一次都可能要交换,而选择排序是在比较时记下a[i]的位置 最后来交换 

所以他们的交换过程是不一样的,但查找的过程是一样的。

所以选择排序的效率比冒泡法只高不低...



(3)插入排序
像扑克摸牌一样,插入即表示将一个新的数据插入到一个有序数组中,并继续保持有序。例如有一个长度为N的无序数组,进行N-1次的插入即能完成排序;第一次,数组第1个数认为是有序的数组,将数组第二个元素插入仅有1个有序的数组中;第二次,数组前两个元素组成有序的数组,将数组第三个元素插入由两个元素构成的有序数组中......第N-1次,数组前N-1个元素组成有序的数组,将数组的第N个元素插入由N-1个元素构成的有序数组中,则完成了整个插入排序。
以下面5个无序的数据为例:
65 27 59 64 58 (文中仅细化了第四次插入过程)
第1次插入: 27 65 59 64 58
第2次插入: 27 59 65 64 58
第3次插入: 27 59 64 65 58
第4次插入: 27 58 59 64 65

void insertsort(int *arr, int nsize)
{
	int i, j, key;
	for (i = 1; i<nsize; i++)//控制需要插入的元素
	{
		key = arr[i]; //key为当前要插入的元素,将其插入到有序段arr[0,i-1]
		j = i - 1;
		while (j >= 0 && arr[j] > key) //查找要插入的位置,循环结束时则找到插入位置j
		{
			arr[j+1] = arr[j]; //移动元素的位置.供要插入元素使用
			j--;
		}
		arr[j+1] = key; //插入指定元素到指定位置
	}
}


(4)快速排序
快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,该方法的基本思想是:
1.先从数列末尾取出一个数作为基准元。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。

//手动快速默写快排:先划界,再分治....  
int quickPartion(int *arr, int low, int high)
{
	int pos = rand() % (high - low + 1) + low;
	swap(arr, pos, high);//将末尾的元素随机化,防止极端情况  
	int key = arr[high];//总是将末尾元素选为关键化界元
	int i = low - 1;//总是记录比key小的元素最前面的位置
	for (int j = low; j <= high - 1; j++)
	{
		if (arr[j] <= key)
			swap(arr, ++i, j);
	}
	swap(arr, ++i, high);
	return i;//返回“中间位置”
}

void QuickSort(int *arr, int low, int high)
{
	if (low < high)
	{
		int mid = quickPartion(arr, low, high);
		QuickSort(arr, low, mid - 1);
		QuickSort(arr, mid + 1, high);
	}
}



(5)归并排序
归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。

核心函数:

//分治法的合并函数  
//arr[low...mid]与arr[mid+1...high]相合并
void Merge(int *arr, int low, int mid, int high)
{
	int leftlen = mid - low + 1;//arr[low...mid]的长度
	int rightlen = high - mid;//arr[mid+1...high]的长度

	int *L = new int[leftlen + 1];//每次归并时都在动态申请内存,这里可以优化
	int *R = new int[rightlen + 1];
	L[leftlen] = INT_MAX;//末尾均是哨兵元素    
	R[rightlen] = INT_MAX;
	//赋值,准备有序放入arr[low.....high]
	int i = 0;
	for (; i < leftlen; i++)
		L[i] = arr[low + i];
	
	int j = 0;
	for (; j < rightlen; j++)
		R[j] = arr[mid + j + 1];

	//有序放入arr[low.....high]
	i = 0; j = 0;
	for (int k = low; k <= high; k++)
	{
		if (L[i] <= R[j])//谁更小,谁就放入arr[k]中
			arr[k] = L[i++];
		else
			arr[k] = R[j++];
	}

	delete[] L; L = NULL;
	delete[] R; R = NULL;
}

//合并排序法(分治法)  
void MergeSort(int *arr, int low, int high)
{
	if (low < high)
	{
		int mid = (low + high) / 2;
		MergeSort(arr, low, mid);
		MergeSort(arr, mid + 1, high);
		Merge(arr, low, mid, high);
	}
}



(6)希尔排序(shell)
希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

//希尔排序  
void ShellSort(int *a,int nlen)
{
	int gap = nlen ;
	while (gap>1)
	{
		gap = gap / 3 + 1;
		for (int i = gap; i < nlen; ++i)
		{
			int temp = a[i]; //暂存关键数据  
			int j = i;
			while (j - gap >= 0 && temp < a[j - gap])
			{
				a[j] = a[j - gap];   //后移  
				j = j - gap;     //前置索引  
			}
			a[j] = temp;
		}
	}
}




(7)堆排序
我们知道堆的结构是节点i的孩子为2*i和2*i+1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n/2-1, n/2-2, ...1这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法。

//保持堆的性质  
//在数组arr中以位置i为入口维护最大堆性质(根节点总是更大)
void  KeepMaxHeap(int *a, int i, int heapsize)  //使a[i]这个元素下降(如果不满足最大根要求的话)    
{
	int l = 2*i+1;
	int r = 2*i+2;

	//找到当前入口位置i及其左右子之间的较大者,如果不是当前i位置的值最大,将会交换并重新开始调整
	int largest =i;
	
	if (l <= heapsize && a[l]>a[i])//与左子比  
		largest = l;

	if (r <= heapsize && a[r]>a[largest])//将较大者与右子比  
		largest = r;
	
	if (largest != i)
	{
		swap(a,i,largest);
		KeepMaxHeap(a, largest, heapsize);
	}
}

//创建堆,将数组调整为最大堆  
void BuildMaxHeap(int *a,int nsize)
{
	int heapsize = nsize - 1;
	for (int i = heapsize / 2; i >= 0; i--)
		KeepMaxHeap(a, i, heapsize);//heapsize/2+1到a.size-1的整数都是树叶,所以只需对0到heapsize/2作处理  
}

//堆排序  
void  HeapSort(int *a,int nsize)
{
	BuildMaxHeap(a,nsize);//使数组成为最大堆  
	int heapsize = nsize-1;
	for (int i = nsize - 1; i>0; i--)
	{
		swap(a,0, i);
		heapsize--;//叶子将逐渐“脱离”
		KeepMaxHeap(a, 0, heapsize);//保持堆的性质  
	}
}




(8)计数排序
假设数序列中小于元素a的个数为n,则直接把a放到第n+1个位置上。当存在几个相同的元素时要做适当的调整,因为不能把所有的元素放到同一个位置上。计数排序假设输入的元素都是0到k之间的整数。
计数排序算法没有用到元素间的比较,它利用元素的实际值来确定它们在输出数组中的位置。因此,计数排序算法不是一个基于比较的排序算法,从而它的计算时间下界不再是Ω(nlogn)。
对每一个输入元素x,确定小于x的元素的个数,那样排序之后,x在最终输出数组中的位置就可以确定了。

例如:如果有17个元素小于x,则x就位于第18个输出的位置上。

//计数排序  
void CountSort(int *a, int len)
{
	int nLen = len;

	int* Cout = new int[nLen];    //申请空间,用于计数,被排序的数一定要是[0,nLen-1]之间的数(可包括) 
	//初始化记数为0  
	for (int i = 0; i < nLen; ++i)
		Cout[i] = 0;

	//统计元素出现次数计数。即数组元素a[i]的出现次数,将其结果存放在Cout[a[i]]中。  
	for (int i = 0; i < nLen; ++i)
		++Cout[a[i]];

	//统计小于等于当前位置i的元素个数  
	for (int i = 1; i < nLen; ++i)
		Cout[i] += Cout[i - 1];

	int* Sort = new int[nLen];    //申请空间,用于存放排序结果  

	for (int i = 0; i < nLen; ++i)
	{
		//把数据放在指定位置。因为pCout[pData[i]]的值就是不比他大数据的个数。  
		//为什么要先减一,因为pCout[pData[i]]保存的是不比他大数据的个数中包括了  
		//他自己,我的下标是从零开始的!所以要先减一。  
		--Cout[a[i]];    //因为有相同数据的可能,所以要把该位置数据个数减一。  
		Sort[Cout[a[i]]] = a[i];

	}

	//排序结束,复制到原来数组中。  
	for (int i = 0; i < nLen; ++i)
		a[i] = Sort[i];

	//释放申请的空间。  
	delete[] Cout;
	Cout = NULL;
	delete[] Sort;
	Sort = NULL;
}



2,排序算法小结

综上,得出结论: 选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,
而冒泡排序、插入排序、计数排序归并排序和基数排序是稳定的排序算法。还有一些排序算法我没有进行

二,C++模板实现

1,程序说明

1),本程序实现了若干经典排序算法性能(运行时间)比较

2),实现了在不同规模(元素个数)下的性能比较

3),实现了对移动次数和比较次数的统计(间接说明性能)

4),实现了数据混乱类型的选择

注:本程序有可能有bug,非绝对正确,发现过错误,未调试完!

2,AllSort.h代码如下

#include "iostream"
#include "vector"
using namespace std;

template<class DataType>  
class AllSort  
{  
public:  
	AllSort()
	{
		//移动次数初始化
		countSortMove=0;
		//比较次数初始化
		countSortCmp=0;
		//初始化数据时的不重复个数
		countNum=5;
	}
	~AllSort()
	{

	}
	//打印数组
	void printArray(vector<DataType> &a,bool isPrint);
	//交换元素  
	void swap(DataType *a,DataType *b);
	//初始化数组
	void InitArr(vector<DataType> &a,int len,int arrType);
	//冒泡排序法
	void BubbleSort(vector<DataType> &a, int l, int r, bool Up=true);
	//插入排序法
	void InsertSort(vector<DataType> &a, int l, int r, bool Up=true);
	//快速排序法
	void QuickSort(vector<DataType> &a, int l, int r, bool Up=true);
	//划界
	int Partition(vector<DataType> &a, int l, int r, bool Up=true);
	//随机快速排序法
	void RandQuickSort(vector<DataType> &a, int l, int r, bool Up=true);
	//随机划界
	int RandPartition(vector<DataType> &a, int l, int r, bool Up=true);
	//三者取中快速排序法
	void thrWayQuickSort(vector<DataType> &a, int l, int r, bool Up=true);
	//三者取中划界
	int thrWayPartition(vector<DataType> &a, int l, int r, bool Up=true);
	//分而治之排序法
	void MergeSort(vector<DataType> &a, int l, int r, bool Up=true);
	void Merge(vector<DataType> &a, int p, int q, int r,bool Up=true);
	//计数排序
	void CountSort(vector<DataType> &a,int arrType,int len,bool Up=true); 
	//洗牌策略随机打乱一个数组中的n个元素 
	void randArray(vector<DataType> &a, unsigned int n,int isRand);
	//选择排序法
	void SelectSort(vector<DataType> &a, int l, int r, bool Up=true);
	//希尔排序
	void ShellSort(vector<DataType> &a,int low, int high,bool Up=true);
	//堆中的左子
	int Left(int i);
	//堆中的右子
	int Right(int i);
	//保持堆性质
	void KeepMaxHeap(vector<DataType> &a, int len,int heapsize,bool Up);
	//建堆
	void BuildMaxHeap(vector<DataType> &a,bool Up);
	//堆排序
	void HeapSort(vector<DataType> &a,bool Up);

	int getSortCount(bool IsMove)
	{
		if (IsMove)
		{
			return countSortMove;
		}else
		{
			return countSortCmp;
		}
	}
	void beforSort()
	{
		//移动次数初始化
		countSortMove=0;
		//比较次数初始化
		countSortCmp=0;
	}

private:
	//移动次数
	int countSortMove;
	//比较次数
	int countSortCmp;
	//初始化数据时的不重复数据个数
	int countNum;
};

//交换元素
template <typename DataType>    
void AllSort<DataType>::swap(DataType *a,DataType *b)
{
	DataType temp;
	temp =*a;
	*a=*b;
	*b=temp;
}

//交换元素
template <typename DataType>    
void AllSort<DataType>::InitArr(vector<DataType> &a,int len,int arrType)
{
	if (arrType == 1)
	{
		for (int i=0;i<len;i++)
		{
			a.push_back(i);
		}
	}else if(arrType == 2)
	{
		for (int i=0;i < len;i++)
		{
			int key=i%countNum;
			a.push_back(key);
		}
	}
}

// 随机打乱一个数组n个元素
template <typename DataType>   
void  AllSort<DataType>::randArray(vector<DataType> &a,unsigned int n,int isRand)
{
	if ( n > a.size() )
	{
		cerr<<"无法打乱指定数量的元素"<<endl;
		exit(1);
	}
	if (isRand == 1)//随机打乱
	{
		unsigned int index, i;
		for (i = 0; i < n; i++)//将数组中的每一个元素都进行随即打乱
		{
			index = rand() % n;//残生0到n-1之间的随机位置数
			if (index != i)
			{
				swap(&a[i],&a[index]);
			}
		}
	}else if(isRand == 2)//升序
	{
		bool upflag=true;
		this->ShellSort(a,0,a.size()-1,upflag);
	}else if(isRand == 3) //降序
	{
		bool upflag=false;
		this->ShellSort(a,0,a.size()-1,upflag);
	}else if(isRand == 4)
	{
		unsigned int gap=5;
		unsigned int index, i;
		for (i = 0; i < n; i++)//将数组中的每一个元素都进行随即打乱
			{
				index = rand()%gap + i;//残生i到j之间的随机位置数
				if ( index != i&& index < n)
				{
					swap(&a[i],&a[index]);
				}
			}
	}else
	{
		cerr<<"isRand参数错误!来自randArray()函数"<<endl;
		return;
	}
}

//打印数组
template <typename DataType>  
void  AllSort<DataType>::printArray(vector<DataType> &a,bool isPrint)
{
	
	if (isPrint)
	{
		cout<<"排序或打乱结果为: ";
		for(unsigned int i = 0; i < a.size(); i++ )  
		{
			cout << a[ i ]<<" ";  
			if ((i+1)%27 == 0)
			{
				cout<<endl;
			}
		}
		cout<<endl;
	}
}

//希尔排序
template <typename DataType>  
void AllSort<DataType>::ShellSort(vector<DataType> &a,int low, int high,bool Up)
{
	beforSort();
	int gap = high-low+1;
	while (gap>1)
	{
		gap = gap/3+1;
		for(int i = low + gap; i <= high; ++i)  
		{  
			int temp = a[i];    //暂存关键数据
			int j = i;  
			countSortCmp++;
			if (Up)
			{
				while(j-gap >= low && temp < a[j-gap])  
				{  
					a[j] = a[j-gap];   //后移
					j = j-gap;     //前置索引
					countSortCmp++;
					countSortMove++;
				}
			}else
			{
				while(j-gap >= low && temp > a[j-gap])  
				{  
					a[j] = a[j-gap];   //后移
					j = j-gap;     //前置索引
					countSortCmp++;
					countSortMove++;
				}
			}
			a[j] = temp;   
			countSortMove=countSortMove+2;
		}  
	}
}  

//随机快速排序划界函数
int stepRandQuickSort=1;
template <typename DataType>    
void AllSort<DataType>::RandQuickSort(vector<DataType> &a, int low, int high, bool Up)
{
	int pivotKey=0;
	if ( stepRandQuickSort ==1)
	{
		beforSort();
		stepRandQuickSort=0;
	}
	if (low < high)
	{
		pivotKey=RandPartition(a,low,high,Up);
		RandQuickSort(a,low,pivotKey-1,Up);
		RandQuickSort(a,pivotKey+1,high,Up);
	}
}

//随机快速排序的中点函数
template <typename DataType>    
int AllSort<DataType>::RandPartition(vector<DataType> &a, int low, int high, bool Up)
{
	int randPivotKey=rand()%(high-low+1)+low;
	this->swap(&a[randPivotKey],&a[high]);
	countSortMove=countSortMove+3;
	return Partition(a,low,high,Up);
}

int stepthrWayQuickSort=1;
template <typename DataType>    
void AllSort<DataType>::thrWayQuickSort(vector<DataType> &a, int low, int high, bool Up)
{
	if (stepthrWayQuickSort ==1)
	{
		beforSort();
		stepthrWayQuickSort=0;
	}
	int pivotKey=0;
	if (low < high)
	{
		pivotKey=thrWayPartition(a,low,high,Up);
		thrWayQuickSort(a,low,pivotKey-1,Up);
		thrWayQuickSort(a,pivotKey+1,high,Up);
	}
}
//快速排序的中点函数
template <typename DataType>    
int AllSort<DataType>::thrWayPartition(vector<DataType> &a, int low, int high, bool Up)
{
	int PivotKey1=rand()%(high-low+1)+low;
	int PivotKey2=rand()%(high-low+1)+low;
	int PivotKey3=rand()%(high-low+1)+low;

	int midkey=PivotKey1;
	if (PivotKey1 <= PivotKey2 && PivotKey2 <= PivotKey3 )
	{
		midkey=PivotKey2;
		countSortCmp=countSortCmp+2;
	}else if( PivotKey2 <= PivotKey1 && PivotKey1 <= PivotKey3 )
	{
		midkey=PivotKey1;
		countSortCmp=countSortCmp+2;
	}else
	{
		midkey=PivotKey3;
	}

	this->swap(&a[midkey],&a[high]);
	countSortMove=countSortMove+3;
	return Partition(a,low,high,Up);
}

//快速排序
int stepQuickSort=1;
template <typename DataType>    
void AllSort<DataType>::QuickSort(vector<DataType> &a, int low, int high, bool Up)
{
	if (stepQuickSort ==1)
	{
		beforSort();
		stepQuickSort=0;
	}
	int mid=0;
	if (low < high)
	{
		mid=Partition(a,low,high,Up);
		QuickSort(a,low,mid-1,Up);
		QuickSort(a,mid+1,high,Up);
	}
}

//快速排序的划界函数
template <typename DataType>    
int AllSort<DataType>::Partition(vector<DataType> &a, int low, int high, bool Up)
{
	int pivotkey=a[high];//选择主元,即基准元素
	int i=low-1;
	if (Up)
	{
		for ( int j = low;j < high;j++)
		{
			countSortCmp++;
			if (a[j] < pivotkey && i != j )
			{
				i++;
				swap(&a[i],&a[j]);
				countSortMove=countSortMove+3;
			}
		}
	}else
	{
		for ( int j = low;j < high;j++)
		{
			countSortCmp++;
			if (a[j] > pivotkey)
			{
				i++;
				swap(&a[i],&a[j]);
				countSortMove=countSortMove+3;
			}
		}
	}

	i++;
	swap(&a[i],&a[high]);
	countSortMove=countSortMove+3;
	return i;
}

int stepMerge=1;
//合并排序法(分治法)
template <typename DataType>    
void AllSort<DataType>::MergeSort(vector<DataType> &a, int low, int high, bool Up)
{
	if (stepMerge==1)
	{
		beforSort();
		stepMerge=0;
	}
	if (low < high)
	{
		int mid=(low+high)/2;
		MergeSort(a,low,mid,Up);
		MergeSort(a,mid+1,high,Up);
		Merge(a,low,mid,high,Up);
	}

}

//分治法的合并函数
template <typename DataType>    
void AllSort<DataType>::Merge(vector<DataType> &a, int low, int mid, int high,bool Up)
{
	int n1=mid -low+1,n2=high-mid;  
	long Max=99999999;
	long Min=-99999999;
	int *L=new int[n1+1];  
	int *R=new int[n2+1];  
	if ( L == NULL || R == NULL )  
	{  
		exit(1);  
	}  
	int i=0;  
	for ( ;i < n1;i++ )  
	{  
		L[i]=a[low+i];  
		countSortMove++;
	}  
	int j=0;  
	for ( ;j < n2;j++ )  
	{  
		R[j]=a[mid+j+1];  
		countSortMove++;
	}  


	i=0;j=0;  
	int k=0;  
	if (Up)
	{
		L[n1]=Max;//哨兵元素位置  
		R[n2]=Max;
		for ( k = low; k <= high ; k++ )  
		{  
			if ( L[i] <= R[j] )  
			{  
				a[k]=L[i];  
				i++;  
				countSortMove++;
			}else  
			{  
				a[k]=R[j];  
				j++;  
				countSortMove++;
			} 
			countSortCmp++;
		}
	}else
	{
		L[n1]=Min;//哨兵元素位置  
		R[n2]=Min;
		for ( k = low; k <= high ; k++ )  
		{  
			if ( L[i] >= R[j] )  
			{  
				a[k]=L[i];  
				i++;  
				countSortMove++;
			}else  
			{  
				a[k]=R[j];  
				j++;  
				countSortMove++;
			} 
			countSortCmp++;
		}
	}

	delete[] L;  
	delete[] R;  
}

//选择排序法
template <typename DataType>    
void AllSort<DataType>::SelectSort(vector<DataType> &a, int low, int high, bool Up)
{
	beforSort();
	DataType min; 
	DataType max;

	for(int i = low ; i <= high; i++)
	{
		//找到最小值及其位置(准备与a[i]调换)
		min = a[i]; 
		max = a[i];
		int index = i;
		for(int j=i+1;j <= high;j++)
		{
			countSortCmp++;
			if (Up)
			{
				if(a[j] < min)
				{ 
					min = a[j]; //当前最小值
					index = j;//当前最小值的位置
					countSortMove++;
				}  
			}else
			{
				if(a[j] > max)
				{ 
					max = a[j]; //当前最大值
					index = j;//当前最大值的位置
					countSortMove++;
				}  
			}
			
		}       

		if (Up)
		{
			swap(&a[i],&a[index]);
			countSortMove=countSortMove+3;
		}else
		{
			swap(&a[i],&a[index]);
			countSortMove=countSortMove+3;
		}
	}       
}

//冒泡排序
template <typename DataType>    
void  AllSort<DataType>::BubbleSort(vector<DataType> &a, int l, int r, bool Up)    
{   
	beforSort();
	bool flag=false;
	for ( int i = l;i < r;i++)  
	{
		flag=false;
		for ( int j = r;j >= i+1;j--)   
		{
			
			countSortCmp++;
			if (Up)
			{
				if (a[j-1]>a[j])  //调换次序  
				{   
					this->swap(&a[j-1],&a[j]);
					countSortMove=countSortMove+3;
					flag=true;
				}  
			}else
			{
				if (a[j-1]<a[j])    
				{   
					this->swap(&a[j-1],&a[j]);
					countSortMove=countSortMove+3;
					flag=true;
				}  
			}
		}
		if (!flag)
		{
			break;
		}
	}
			 
}  


//插入排序
template <typename DataType>    
void  AllSort<DataType>::InsertSort(vector<DataType> &a, int l, int r, bool Up)    
{   
	beforSort();
	DataType key;
	if (Up)   
	{   
		for ( int i = l+1 ; i <= r;i++ )
		{
			key=a[i];
			int j=i-1;
			countSortCmp++;
			while ( j >= l && a[j] > key )
			{
				a[j+1]=a[j];
				j--;
				countSortMove++;
				countSortCmp++;
			}
			a[j+1]=key;
			countSortMove=countSortMove+2;
		}
	}   
	else   
	{
		for ( int i = l+1 ; i <= r;i++ )
		{
			key=a[i];
			int j=i-1;
			countSortCmp++;
			while (j >= l && a[j] < key)
			{
				a[j+1]=a[j];
				j--;
				countSortMove++;
				countSortCmp++;
			}
			a[j+1]=key;
			countSortMove=countSortMove+2;
		}
	}
}


template <typename DataType>    
int  AllSort<DataType>::Left( int i)    
{   
	
	return 2*i+1;
}

template <typename DataType>    
int  AllSort<DataType>::Right( int i)    
{   
	return 2*i+2;
}
   
//保持堆的性质
template <typename DataType>    
void  AllSort<DataType>::KeepMaxHeap(vector<DataType> &a, int i,int heapsize,bool Up)  //使a[i]这个元素下降(如果不满足最大根要求的话)  
{   
	int l=Left(i);
	int r=Right(i);
	int largest=0;
	int min=0;
	if (Up)
	{
		if (l<= heapsize && a[l]>a[i])//与左子比
		{
			largest=l;
		}
		else
		{
			largest=i;
		}

		if (r<= heapsize && a[r]>a[largest])//将较大者与右子比
		{
			largest=r;
		}
	}else
	{
		if (l<= heapsize && a[l]<a[i])//与左子比
		{
			min=l;
		}
		else
		{
			min=i;
		}

		if (r<= heapsize && a[r]<a[min])//将较大者与右子比
		{
			min=r;
		}
	}
	
	countSortCmp=countSortCmp+2;
	if (Up)
	{
		if (largest!=i)
		{
			swap(&a[i],&a[largest]);
			countSortMove=countSortMove+3;
			KeepMaxHeap(a,largest,heapsize,Up);
		}
	}else
	{
		if (min!=i)
		{
			swap(&a[i],&a[min]);
			countSortMove=countSortMove+3;
			KeepMaxHeap(a,min,heapsize,Up);
		}
	}
	
}

//创建堆,将数组调整为最大堆
template <typename DataType>    
void  AllSort<DataType>::BuildMaxHeap(vector<DataType> &a,bool Up)    
{   
	int heapsize=a.size()-1;
	for (int i=heapsize/2 ; i>=0 ;i-- )
	{
		KeepMaxHeap(a,i,heapsize,Up);//heapsize/2+1到a.size-1的整数都是树叶,所以只需对0到heapsize/2作处理
	}
}

//堆排序
template <typename DataType>    
void  AllSort<DataType>::HeapSort(vector<DataType> &a,bool Up)    
{   
	beforSort();
	BuildMaxHeap(a,Up);//使数组成为最大堆
	int heapsize=a.size()-1;
	for (int i=a.size()-1;i>0;i--)
	{
		swap(&a[0],&a[i]);
		countSortMove=countSortMove+3;
		heapsize--;
		KeepMaxHeap(a,0,heapsize,Up);//保持堆的性质
	}
}


//计数排序
template <typename DataType>   
void AllSort<DataType>::CountSort(vector<DataType> &a,int arrType,int len,bool Up)
{
	int nLen=len;
	int k=0;
	if (arrType==1)
	{
		k=nLen;
	}else
	{
		k=countNum;
	}

	countSortCmp=0;
	DataType* Cout = new DataType[nLen];    //申请空间,用于计数
	//初始化记数为0
	for (int i = 0; i < k; ++i)
	{
		Cout[i] = 0;
	}
	//统计元素出现次数计数。即数组元素a[i]的出现次数,将其结果存放在Cout[a[i]]中。
	for (int i = 0; i < nLen; ++i)
	{
		++Cout[a[i]];        
	}

	//统计小于等于当前位置i的元素个数
	for (int i = 1; i < nLen; ++i)
	{
		Cout[i] += Cout[i - 1];    
	}

	DataType* Sort =new DataType[nLen];    //申请空间,用于存放排序结果

	for (int i = 0; i < nLen; ++i)
	{
		//把数据放在指定位置。因为pCout[pData[i]]的值就是不比他大数据的个数。
		//为什么要先减一,因为pCout[pData[i]]保存的是不比他大数据的个数中包括了
		//他自己,我的下标是从零开始的!所以要先减一。
		--Cout[a[i]];    //因为有相同数据的可能,所以要把该位置数据个数减一。
		Sort[Cout[a[i]]] = a[i];        

	}

	//排序结束,复制到原来数组中。
	if (Up==true)
	{
		for (int i = 0; i < nLen; ++i)
		{
			a[i] = Sort[i];
		}
	}else
	{
		for (int i = 0; i < nLen; ++i)
		{
			a[i] = Sort[nLen-i-1];
		}
	}

	//释放申请的空间。
	delete[] Cout;
	Cout=NULL;
	delete[] Sort;
	Sort=NULL;
}


3,主测试代码:

// ConsoleAppAllSort.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "iostream"  
#include <vector>  
#include <windows.h> 
#include "AllSort.h"
#include "stdio.h"
#include<algorithm>
#include<functional>
using namespace std;

LARGE_INTEGER freq;  
LARGE_INTEGER startTime, stopTime;  
double time;  

void help(int len,bool Up,int IsRand,int arrType);
void initArr();
int _tmain(int argc, _TCHAR* argv[])   
{  
	system("color 0A");
	AllSort<int> sort;//定义排序类对象
	vector<int> arr;       //定义并初始化一个容器,进行数组操作
	int len=10;    //控制数组长度
	char ans='y';
	do 
	{
		cout<<"请输入数组初始化类型及元素个数:(1为元素完全不重复,2为有大量重复值)"<<endl;
		int arrType=1;//1为完全不重复,2为大量重复值
		cin>>arrType>>len;
		sort.InitArr(arr,len,arrType);

		//各大开关
		cout<<"请输入排序类型:0为降序,1为升序"<<endl;
		bool upflag=false;//控制所有排序的升排或者降排的开关
		cin>>upflag;
		cout<<"请输入数组的随机类型:1为纯随机,2为升序,3为降序,4为小随机"<<endl;
		int isRand=1;//1为纯随机,2为升序,3为降序,4为小随机
		cin>>isRand;
		cout<<"是否打印排序结果:0为否,1为是"<<endl;
		bool isPrint=false;//是否print
		cin>>isPrint;
		bool isMove=true;
		help(len,upflag,isRand,arrType);

		cout<<"/**************************改进冒泡法***************************/"<<endl;
		sort.randArray(arr,len,isRand);
		sort.printArray(arr,isPrint);
		QueryPerformanceCounter(&startTime);  
		sort.BubbleSort(arr,0,arr.size()-1,upflag);
		QueryPerformanceCounter(&stopTime);  
		sort.printArray(arr,isPrint);
		time = 1e3*(stopTime.QuadPart-startTime.QuadPart)/freq.QuadPart;  
		cout<<"消耗时间"<<time<<"毫秒   ";
		cout<<"移动次数:"<<sort.getSortCount(isMove)<<"   比较次数"<<sort.getSortCount(!isMove)<<endl;

		cout<<"/*******************std::sort()(注:无法统计次数)******************/"<<endl;
		sort.beforSort();
		sort.randArray(arr,len,isRand);
		sort.printArray(arr,isPrint);
		QueryPerformanceCounter(&startTime);  
		if (upflag)
		{
			std::sort(arr.begin(),arr.end(),std::less<int>());
		}else
		{
			std::sort(arr.begin(),arr.end(),std::greater<int>());
		}
		
		QueryPerformanceCounter(&stopTime);  
		sort.printArray(arr,isPrint);
		time = 1e3*(stopTime.QuadPart-startTime.QuadPart)/freq.QuadPart;  
		cout<<"消耗时间"<<time<<"毫秒   ";
		cout<<"移动次数:"<<sort.getSortCount(isMove)<<"   比较次数"<<sort.getSortCount(!isMove)<<endl;

		cout<<"/**************************插入法***************************/"<<endl;
		sort.randArray(arr,len,isRand);//打乱数组
		sort.printArray(arr,isPrint);
		QueryPerformanceCounter(&startTime);  
		sort.InsertSort(arr,0,arr.size()-1,upflag);
		QueryPerformanceCounter(&stopTime);  
		sort.printArray(arr,isPrint);
		time = 1e3*(stopTime.QuadPart-startTime.QuadPart)/freq.QuadPart;  
		cout<<"消耗时间"<<time<<"毫秒   ";
		cout<<"移动次数:"<<sort.getSortCount(isMove)<<"   比较次数"<<sort.getSortCount(!isMove)<<endl;

		cout<<"/**************************分治法***************************/"<<endl;
		sort.randArray(arr,len,isRand);//打乱数组
		sort.printArray(arr,isPrint);
		QueryPerformanceCounter(&startTime); 
		sort.MergeSort(arr,0,arr.size()-1,upflag);
		QueryPerformanceCounter(&stopTime); 
		sort.printArray(arr,isPrint);
		time = 1e3*(stopTime.QuadPart-startTime.QuadPart)/freq.QuadPart;  
		cout<<"消耗时间"<<time<<"毫秒   ";
		cout<<"移动次数:"<<sort.getSortCount(isMove)<<"   比较次数"<<sort.getSortCount(!isMove)<<endl;

		cout<<"/********************快排法(随机划界元)**********************/"<<endl;
		sort.randArray(arr,len,isRand);//打乱数组
		sort.printArray(arr,isPrint);
		QueryPerformanceCounter(&startTime); 
		sort.RandQuickSort(arr,0,arr.size()-1,upflag);
		QueryPerformanceCounter(&stopTime); 
		sort.printArray(arr,isPrint);
		time = 1e3*(stopTime.QuadPart-startTime.QuadPart)/freq.QuadPart;  
		cout<<"消耗时间"<<time<<"毫秒   ";
		cout<<"移动次数:"<<sort.getSortCount(isMove)<<"   比较次数"<<sort.getSortCount(!isMove)<<endl;

		cout<<"/********************快排法(三数取中划界元)*******************/"<<endl;
		sort.randArray(arr,len,isRand);//打乱数组
		sort.printArray(arr,isPrint);
		QueryPerformanceCounter(&startTime); 
		sort.thrWayQuickSort(arr,0,arr.size()-1,upflag);
		QueryPerformanceCounter(&stopTime); 
		sort.printArray(arr,isPrint);
		time = 1e3*(stopTime.QuadPart-startTime.QuadPart)/freq.QuadPart;  
		cout<<"消耗时间"<<time<<"毫秒   ";
		cout<<"移动次数:"<<sort.getSortCount(isMove)<<"   比较次数"<<sort.getSortCount(!isMove)<<endl;

		cout<<"/********************快排法(尾端划界元)*************************/"<<endl;
		sort.randArray(arr,len,isRand);//打乱数组
		sort.printArray(arr,isPrint);
		QueryPerformanceCounter(&startTime); 
		sort.QuickSort(arr,0,arr.size()-1,upflag);
		QueryPerformanceCounter(&stopTime); 
		sort.printArray(arr,isPrint);
		time = 1e3*(stopTime.QuadPart-startTime.QuadPart)/freq.QuadPart;  
		cout<<"消耗时间"<<time<<"毫秒   ";
		cout<<"移动次数:"<<sort.getSortCount(isMove)<<"   比较次数"<<sort.getSortCount(!isMove)<<endl;

		cout<<"/*****************************选择法****************************/"<<endl;
		sort.randArray(arr,len,isRand);//打乱数组
		sort.printArray(arr,isPrint);
		QueryPerformanceCounter(&startTime); 
		sort.SelectSort(arr,0,arr.size()-1,upflag);
		QueryPerformanceCounter(&stopTime); 
		sort.printArray(arr,isPrint);
		time = 1e3*(stopTime.QuadPart-startTime.QuadPart)/freq.QuadPart;  
		cout<<"消耗时间"<<time<<"毫秒   ";
		cout<<"移动次数:"<<sort.getSortCount(isMove)<<"   比较次数"<<sort.getSortCount(!isMove)<<endl;

		cout<<"/******************************希尔法***************************/"<<endl;
		sort.randArray(arr,len,isRand);//打乱数组
		sort.printArray(arr,isPrint);
		QueryPerformanceCounter(&startTime); 
		sort.ShellSort(arr,0,arr.size()-1,upflag);
		QueryPerformanceCounter(&stopTime); 
		sort.printArray(arr,isPrint);
		time = 1e3*(stopTime.QuadPart-startTime.QuadPart)/freq.QuadPart;  
		cout<<"消耗时间"<<time<<"毫秒   ";
		cout<<"移动次数:"<<sort.getSortCount(isMove)<<"   比较次数"<<sort.getSortCount(!isMove)<<endl;

		cout<<"/******************************堆排序法***************************/"<<endl;
		sort.randArray(arr,len,isRand);//打乱数组
		sort.printArray(arr,isPrint);
		QueryPerformanceCounter(&startTime); 
		sort.HeapSort(arr,upflag);
		QueryPerformanceCounter(&stopTime); 
		sort.printArray(arr,isPrint);
		time = 1e3*(stopTime.QuadPart-startTime.QuadPart)/freq.QuadPart;  
		cout<<"消耗时间"<<time<<"毫秒   ";
		cout<<"移动次数:"<<sort.getSortCount(isMove)<<"   比较次数"<<sort.getSortCount(!isMove)<<endl;



		cout<<"/******************************计数序法***************************/"<<endl;
		sort.randArray(arr,len,isRand);//打乱数组
		sort.printArray(arr,isPrint);
		QueryPerformanceCounter(&startTime); 
		sort.CountSort(arr,arrType,len,upflag);
		QueryPerformanceCounter(&stopTime); 
		sort.printArray(arr,isPrint);
		time = 1e3*(stopTime.QuadPart-startTime.QuadPart)/freq.QuadPart;  
		cout<<"消耗时间"<<time<<"毫秒   ";
		cout<<"移动次数:"<<sort.getSortCount(isMove)<<"   比较次数"<<sort.getSortCount(!isMove)<<endl;

		cout<<"是否继续(y/n)?"<<endl;
		cin>>ans;
		arr.clear();
	} while (ans=='y');
	
	system("pause");
	return 0;  
}  

void help(int len,bool Up,int IsRand,int arrType)
{
	cout<<"/***********************综上所述******************************"<<endl;
	cout<<"当前数组元素个数为:"<<len<<endl;
	cout<<"原始数组的元素特点:"<<endl;
	if (arrType==1)
	{
		cout<<"   1)无重复值"<<endl;
		cout<<"   2)值分布:0-"<<len-1<<endl;
	}else if(arrType==2)
	{
		cout<<"   1)大量重复值"<<endl;
		cout<<"   2)非重复值的分布:0-5"<<endl;
	}
	
	if (IsRand == 1)
	{
		cout<<"   3)随机分布"<<endl;
	}else if(IsRand ==2)
	{
		cout<<"   3) 有序:升序"<<endl;
	}else if(IsRand ==3)
	{
		cout<<"   3) 有序:降序"<<endl;
	}
	else if(IsRand ==4)
	{
		cerr<<"   3) 小随机(总体上呈有序趋势)"<<endl;
	}else
	{
		cerr<<"IsRand参数错误!"<<endl;
	}

	if (Up)
	{
		cout<<"各排序即将执行:升序"<<endl;
	}else
	{
		cout<<"各排序即将执行:降序"<<endl;
	}

	QueryPerformanceFrequency(&freq);  
	QueryPerformanceCounter(&startTime);  
	Sleep(500);  
	QueryPerformanceCounter(&stopTime);  
	time = 1e3*(stopTime.QuadPart-startTime.QuadPart)/freq.QuadPart;  
	cout<<"你电脑的频率为:"<<freq.QuadPart<<endl;
	cout<<"你的电脑执行Sleep(500)消耗时间为:"<<time<<"毫秒"<<endl;
}




三,测试结果

(篇幅有限,仅展示部分结果)




四,总结:

1、冒泡排序 
 优点:稳定; 思想易理解,编写简单
 缺点:慢,每次只能移动相邻两个数据。

2、选择排序 
 优点:编写简单;移动数据的次数已知(n-1次);  
 缺点:比较次数多。

3、插入排序 
优点:稳定,思想简单,编写简单。
缺点:比较次数不一定,比较次数越少,插入点后的数据移动越多,特别是当数据总量庞大的时候,但用链表可以解决这个问题。插入排序不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,例如,量级小于千,那么插入排序还是一个不错的选择。

4、希尔排序
优点:快,数据移动少; 
缺点:不稳定,gap的取值是多少,应取多少个不同的值,都无法确切知道,只能凭经验来取,并且无法对链表排序。

5、快速排序 
快速排序是冒泡排序的改进版,是目前已知的最快的排序方法。
优点:极快,数据移动少;
缺点:不稳定。

6、归并排序   
   归并排序是多次将两个或两个以上的有序表合并成一个新的有序表。最简单的归并是直接将两个有序的子表合并成一个有序的表。 
   归并排序是稳定的排序.即相等的元素的顺序不会改变.如输入记录 1(1) 3(2) 2(3) 2(4) 5(5) (括号中是记录的关键字)时输出的 1(1) 2(3) 2(4) 3(2) 5(5) 中的2 和 2 是按输入的顺序.这对要排序数据包含多个信息而要按其中的某一个信息排序,要求其它信息尽量按输入的顺序排列时很重要.这也是它比快速排序优势的地方. 
归并排序:归并排序是一种非就地排序,将需要与待排序序列一样多的辅助空间。在使用它对两个己有序的序列归并,将有无比的优势。其时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlog2n)。对数据的有序性不敏感。若数据节点数据量大,那将不适合。但可改造成索引操作,效果将非常出色。

7、堆排序:

由于它在直接选择排序的基础上利用了比较结果形成。效率提高很大。它完成排序的总比较次数为O(nlog2n)。它是对数据的有序性不敏感的一种算法。但堆排序将需要做两个步骤:
一是建堆,二是交换数值(或者输出堆),三是排序(调整堆)。
所以一般在小规模的序列中不合适,但对于较大的序列,将表现出优越的性能。

8、计数排序:

对于数组元素集中在小范围内的整数数组排序特别快,并且也是稳定排序,但是这种排序算法缺点特别大。


参考资源:

【1】《算法导论》
【2】《维基百科》
【3】《百度文库》
【4】http://www.oschina.net/question/1397765_159365
【5】http://www.cnblogs.com/biyeymyhjob/archive/2012/07/17/2591457.html
【6】http://blog.csdn.net/xiazdong/article/details/8462393
【7】http://baike.baidu.com/view/297739.htm
【8】http://www.cnblogs.com/kaituorensheng/archive/2013/02/23/2923877.html
【9】http://blog.csdn.net/hguisu/article/details/7776068
【10】http://blog.csdn.net/speedme/article/details/23021467

注:

本文部分文字参考并整理自网络,代码参考并改编自算法导论.

如果侵犯了您的版权,请联系本人tangyibiao520@163.com,本人将及时编辑掉!



版权声明:本文为EbowTang原创文章,后续可能继续更新本文。如果转载,请务必复制本文末尾的信息!

经典的7种排序算法 原理C++实现

经典的7种排序算法 原理C++实现 排序是编程过程中经常遇到的操作,它在很大程度上影响了程序的执行效率。 7种常见的排序算法大致可以分为两类:第一类是低级排序算法,有选择排序、冒泡排序、插入排序;第...

各种内排序算法的C++实现(不断更新中)

一.插入排序(insert sorting)#include using namespace std;/*交换函数,作用是交换数组中的两个元素的位置*/void swap(int array[],in...

C/C++的八种排序算法及实现

几个基本常见的排序,插入排序 (包括 直接插入,希尔插入,折半插入等),交换排序(包括 冒泡排序,快速排序) ,选择排序(简单选择,堆排序,树形排序等),归并排序,基数排序(多关键字,链式基数)。...
  • zcyzsy
  • zcyzsy
  • 2016年10月08日 22:43
  • 1649

C/C++ 八大排序算法

转自 http://blog.csdn.net/hguisu/article/details/7776068 目录(?)[+] 概述 排序有内部排序和外部排序,内部排序是数据记录在内...

C++ 排序算法

经典排序算法总结(代码) ·冒泡法 ·快速排序 ·插入排序 ·希尔(shell)排序 ·选择排序 ·堆排序 ·归并排序   附: 排序算法原理:http://zh.wiki...

九大经典排序算法整理及其源代码分享(C/C++版)

本次对数据结构与算法中常见的九大排序算法做了一次总结,并用C++进行实现。...

C++实现——排序算法总结

/* 常见的排序算法有:直接插入 希尔 冒泡 快速 选择 堆排序 归并 基数 *///下面一一分析,并实现: /* 1.冒泡排序 冒泡排序是最简单的排序算法,冒泡排序的基本思想是从后往前(或从...

【C++研发面试笔记】19. 常用算法-排序算法

【C++研发面试笔记】19. 常用算法-排序算法19.1 排序算法分类比较排序和非比较排序: 常见的排序算法都是比较排序,非比较排序包括计数排序、桶排序和基数排序,非比较排序对数据有要求,因为数据本...
  • tostq
  • tostq
  • 2016年10月03日 23:12
  • 1666

数据结构——算法之(043)(c++各种排序算法实现)

【申明:本文仅限于自我归纳总结和相互交流,有纰漏还望各位指出。 联系邮箱:Mr_chenping@163.com】 题目: c++ 各种排序算法实现 题目分析: 具体排序原理参考相...

c++ 排序算法

交换排序 冒泡排序( Bubble Sort) 思想:通过交换相邻位置的数,把大的值往后交换,就像是在冒泡 时间复杂度:最差 O(n^2),最好 O(n),平均 O(n^2) 算法稳定性:相同元...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:<C/C++算法> 八大经典排序算法的性能对比与总结
举报原因:
原因补充:

(最多只允许输入30个字)