十大排序算法解析与实现

未完待续…

#include<string.h>
#include<iostream>
#include<stdio.h>
#include<malloc.h>
#include<Error.h>
#include<stdlib.h>
#include<Windows.h>
#include<string.h>
#include<time.h>
#include<wtypes.h>
#include<algorithm>
#include<assert.h>
#include<math.h>

using namespace std;

//如无特殊说明,所有排序均为从小到大排序
//我们通常所说的排序算法往往指的是内部排序算法,即数据记录在内存中进行排序。
/*
排序算法大体可分为两种:

一种是比较排序,时间复杂度O(nlogn) ~ O(n^2),主要有:冒泡排序,选择排序,插入排序,希尔排序,归并排序,堆排序,快速排序等。

另一种是非比较排序,时间复杂度可以达到O(n),主要有:计数排序,基数排序,桶排序等

排序算法有一个算法稳定性的性质。算法稳定性就是指:在排序前有两个元素Ri ,Rj,他们的关键字ki,kj,且ki=kj。
如果在排序前Ri在Rj的前面,在排序后Ri仍然在Rj的前面,就说这个排序算法是稳定的。但是并不是说稳定的算法就是好的算法,这只是排序算法的一个性质而已。
*/


static int comparecount;	//比较次数
static int swapcount;		//交换次数
static int executecount;	//执行次数

const int n = 100;
const int MaxRandom = 1000;

//生成随机整数数组
int* generateRandomArray(int n, int min,int max)   
{
	assert(n>0 && min <= max);

	int *arr = new int[n];

	srand(time(NULL));		//使用当前时间进行随机数发生器的初始化

	for(int i=0; i<n; i++)
	if(min==0)
		arr[i] = rand()%(max+1);
	else
		arr[i] = rand()%(max-min+1) + min;

	return arr;
}

//生成随机整数
int randint(int min,int max)          //srand()放在主函数中了
{
	if(min==0)
		return rand()%(max+1);
	else
		return rand()%(max-min+1) + min;
}

//打印
void Print(int array[], int n)
{
	assert(n);

	for(int i = 0; i<n; i++)
		cout<<array[i]<<" ";
	cout<<endl;
}


///比较排序

//冒泡排序          -------这不是冒泡排序    什么排序呢?????----------相当于选择排序
void BubbleSort(int array[], int n)
{
	assert(n);
	
	comparecount = 0;	//比较次数
	swapcount = 0;		//交换次数

	for(int i=0; i<n; i++)
	{
		for(int j=n-1; j>i; j--)
		{
			comparecount++;
			if(array[i]>array[j])
			{
				swap(array[i],array[j]);
				swapcount++;
			}
		}
	}
}

//冒泡排序--------更贴近“冒泡”描述的排序思路   时间复杂度O(N^2) 空间复杂度O(1)  稳定排序
/*	它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
	这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

	冒泡排序算法的原理如下: [1] 
	比较相邻的元素。如果第一个比第二个大,就交换他们两个。 [1] 
	对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 [1] 
	针对所有的元素重复以上的步骤,除了最后一个。 [1] 
	持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

	冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。
	所以,如果两个元素相等,是不会再交换的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,
	所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。
*/
void BubbleSort1(int array[], int n)
{
	assert(n);
	
	comparecount = 0;	//比较次数
	swapcount = 0;		//交换次数
	bool flag = true;			//好像没什么用

	for(int i=0; i<n  && flag; i++)
	{
		for(int j=n-1; j>i; j--)
		{
			comparecount++;

			//flag = false;
			if(array[j-1]>array[j])
			{
				swap(array[j-1],array[j]);
				flag = true;

				swapcount++;
			}
		}
	}
}

//常规冒泡排序实现
void BubbleSort2(int arr[], int n)
{
	comparecount = 0;	//比较次数
	swapcount = 0;		//交换次数

	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			comparecount++;
			if (arr[j] > arr[j + 1]) 
			{
				swap(arr[j],arr[j+1]);
			
				swapcount++;
			}
		}
	}
}


//选择排序					时间复杂度O(N^2) 空间复杂度O(1)  不稳定排序
/*
选择排序(Selection-sort) 是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕

算法描述
n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:
步骤1:初始状态:无序区为R[1…n],有序区为空;
步骤2:第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,
使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
步骤3:n-1趟结束,数组有序化了

稳定性
在一趟选择,如果一个元素比当前元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。
比较拗口,举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中两个5的相对前后顺序就被破坏了,所以选择排序是一个不稳定的排序算法。
*/
void SelectionSort(int arr[], int n)
{
	assert(n);

	comparecount = 0;
	swapcount = 0;

	int minIndex;		//记录最小元素下标

	for(int i = 0; i<n-1; i++)		//要想数组有序,则最后要求在区间[0,n-1]内是有序的,所以索引i必须从 0 到 n-2 的闭区间。i初始化为0,表示满足有序的的区间不存在;
	{								//i最后为n-2时,最后只有一个元素,那么其肯定为最大的元素,此时整个数组即为排好序的数组。
		minIndex = i;
		for(int j = i+1; j<n; j++)	//选择排序基于这样一个事实:在区间[0,i-1]内是排过序的,且[0,i-1]区间内的元素均要小于区间[i,n-1]内的元素,
		{							//所以必须在区间[i,n-1]内查找出最小的元素,其索引j为从 i 到 n-1 的闭区间,然后找到最小元素将其放到位置i上。
			comparecount++;			//但是这里可以从区间[i+1,n-1]的闭区间里查找最小的元素,然后与位置i上的元素做比较,将两者最小值放到位置i上,这样可以减少些循环量
			
			if(arr[minIndex]>arr[j])
			{
				minIndex = j;
			}
		}

		if(arr[i]>arr[minIndex])	//或者判断 if(i != minIndex)        //当最小元素恰好位于位置i上,则不需要执行交换操作 
		{
			swap(arr[i],arr[minIndex]);
			swapcount++;
		}
	}
}


/*
直接插入排序--------当数组比较小时,插入排序比快速排序快    时间复杂度O(N^2) 空间复杂度O(1)  稳定排序
所谓插入排序,就是不断的依次将元素插入前面已排好序的序列中
插入排序由N-1躺(pass)排序组成。
对于P=1躺到P=N-1躺,插入排序保证从位置0到位置P上的元素为已排序状态。
插入排序利用了这样的事实:位置0到位置P-1上的元素是已排过序的。

具体操作:
1. 比较L[i]与L[i-1],如果L[i]<L[i-1],说明L[i]需要放到前面的有序序列中,执行下一步。
2.根据L[i]的值找到它应该插入的位置,假设为L[k]。
3.将L(k ~ i-1)中的元素全部往后移一个,给L[k]腾出位置。
4.将L[i]的值拷贝到L[k]。
*/
void InsertSort(int arr[], int n)
{
	comparecount = 0;
	swapcount = 0;

	int Tmp,j;

	for(int P = 1; P<n; P++)	//排序趟数P,循环内先将位置P上的元素用Tmp保存起来,然后将大于Tmp的数向右移动,然后直接将Tmp(位置P上的元素)插入到区间[0,P-1]正确的位置上
	{	//在区间[0,P-1]内的元素是已排过序的

		Tmp = arr[P];							//保持位置P上的元素
		for(j=P; j>0 && arr[j-1] > Tmp; j--)	//从P-1 向左依次比较Tmp与该位置(j-1)上元素的大小,如果该位置(j-1)元素大,则依次向右移动一个位置,直到数组头或者Tmp元素大则停止移动。
		{
			arr[j] = arr[j-1];

			comparecount++;
			swapcount++;
		}
		arr[j] = Tmp;							//然后将Tmp值插入到停止移动后 小于Tmp的最大元素位置(j-1) 后面一个位置(j)上,或者是数组首元素。

		//在区间[0,P]内的元素是已排过序的
	}
}

//折半插入排序(二分插入排序)					时间复杂度O(N^2) 空间复杂度O(1)  稳定排序
/*
基本概念:
折半插入排序(binary insertion sort)是对插入排序算法的一种改进,由于排序算法过程中,就是不断的依次将元素插入前面已排好序的序列中。
由于前半部分为已排好序的数列,这样我们不用按顺序依次寻找插入点,可以采用折半查找的方法来加快寻找插入点的速度。
具体操作:
在将一个新元素插入已排好序的数组的过程中,寻找插入点时,将待插入区域的首元素设置为a[low],末元素设置为a[high],则轮比较时将待插入元素与a[m],
其中m=(low+high)/2相比较,如果比参考元素小,则选择a[low]到a[m-1]为新的插入区域(即high=m-1),否则选择a[m+1]到a[high]为新的插入区域(即low=m+1),
如此直至low<=high不成立,即将此位置之后所有元素后移一位,并将新元素插入a[high+1]。
稳定性和复杂度:
折半插入排序算法是一种稳定的排序算法,比直接插入算法明显减少了关键字之间比较的次数,因此速度比直接插入排序算法快,但记录移动的次数没有变,所以折半插入排序算法的时间复杂度仍然为O(n^2),与直接插入排序算法相同。

排序思想:有一组数据待排序,排序区间为Array[0]~Array[n-1]。将数据分为有序数据和无序数据,第一次排序时默认Array[0]为有序数据,Array[1]~Array[n-1]为无序数据。有序数据分区的第一个元素位置为low,最后一个元素的位置为high。

遍历无序区间的所有元素,每次取无序区间的第一个元素Array[i],因为0~i-1是有序排列的,所以用中点m将其平分为两部分,然后将待排序数据同中间位置为m的数据进行比较,若待排序数据较大,
则low~m-1分区的数据都比待排序数据小,反之,若待排序数据较小,则m+1~high分区的数据都比 待排序数据大,此时将low或high重新定义为新的合适分区的边界,对新的小分区重复上面操作。
直到low和high 的前后顺序改变,此时high+1所处位置为待排序数据的合适位置。

思想与直接插入排序相似。区别是直接插入排序是边比较边移动元素,折半插入排序则是将比较和插入的操作分离出来。先通过折半查找找到元素该插入的位置,再统一将该位置及后面的元素后移。 

时间复杂度:O(n^2)
稳定性:稳定。

大都数排序算法只适用于顺序存储的表,而直接插入排序可用于顺序存储的表排序也可用于链式存储的表排序。折半插入排序只适用于顺序存储的表的排序。
一般数据量较小的表中,折半插入排序的性能更优。
*/
void BinaryInsertionSort(int arr[], int n)
{
	comparecount = 0;
	swapcount = 0;

	int Tmp,j;
	int low,high,middle;

	for(int P = 1; P<n; P++)	//排序趟数P,循环内先将位置P上的元素用Tmp保存起来,然后将大于Tmp的数向右移动,然后直接将Tmp(位置P上的元素)插入到区间[0,P-1]正确的位置上
	{	//在区间[0,P-1]内的元素是已排过序的
		
		//用折半查找法(二分查找法)查找到位置P上的元素应该插入的位置
		low = 0;
		high = P-1;
		while(low <= high)	
		{
			comparecount++;

			middle = (low+high)/2;
			if( arr[middle] > arr[P] )
				high = middle -1;
			else
				low = middle + 1;
		}

		//统一将有序区中大于该元素的所有元素向后移动一个位置,然后将该元素插入到正确的位置
		Tmp = arr[P];							//保持位置P上的元素
		for(j=P; j > high + 1; j--)				//统一将有序区中大于该元素的所有元素向后移动一个位置
		{
			arr[j] = arr[j-1];

			swapcount++;
		}
		arr[j] = Tmp;							//将Tmp值插入到正确的位置

		//在区间[0,P]内的元素是已排过序的
	}
}


//希尔排序(基于直接插入排序的优化排序)
/*
	它通过比较相距一定间隔的元素来工作,各趟比较所用的距离随着算法的进行而减少,直到直比较相邻元素的最后一趟排序为止。
由于这个原因,希尔排序有时也叫作缩小增量排序(diminishing increment sort)。
是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。
	希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
	希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。
	希尔排序使用一个序列h1,h2,…,ht,叫做增量序列(increment sequence)。
	
	因为是将数组分割成t份子数组,所以增量序列的最大值ht必须小于数组长度N,然后依次缩小到h1=1

	希尔排序有多种增量序列:          可见网络链接 https://blog.csdn.net/Foliciatarier/article/details/53891144
	希尔(Shell)增量:
		最大增量:ht = N/2       通项公式:hk = 2^k    递推公式:h(k) = h(k+1)/2	证明:h(k) = 2^k = 2*2^(k-1) = h(k-1)
		如:{1,2,4,8...}        最坏情形 O(n^2)
	Hibbard增量:
		最大增量:ht = log2(n+1)的整数部分		通项公式:hk=2^k−1(k为排序趟数)    递推公式:h(k) = (h(k+1)-1)/2	证明:h(k) = 2^k-1 = 2*2^(k-1)-1 = 2*(2^(k-1)-1)+1 = 2*h(k-1)+1		
		如:{1,3,7,…,2^k-1}           最坏情形 O(n^3/2)
	Sedgewick增量:该序列中的项或者是9*4^j-9*2^j+1,或者是4^k-3*2^k+1(两者取大值)	递推公式:
		如:{1,5,19,41,109,…}          最坏情形 O(n^4/3)
	Knuth 增量:
		最大增量:ht = log3(2n+1)的整数部分		通项公式:h(k)=(3^k−1)/2(k为排序趟数)    递推公式:h(k)=(h(k+1)-1)/3	  证明:h(k) = (3^k-1)/2 = (3*3^(k-1)-1)/2 = 3(3^(k-1)-1)/2+1 = 3*( (3^(k-1)-1)/2 )+1 = 3*h(k-1)+1
		如:{1,4,13,40,121,…}
	Gonnet 增量序列

	希尔排序的平均运行时间是 O(n^3/2)
*/

//使用希尔增量的希尔排序
void ShellSortByHillIncrement(int arr[], int n)
{
	int i, j;
	int Increment;		//希尔增量
	int tmp;			//保存当前比较趟次位置i上的元素值

	comparecount = 0;
	swapcount = 0;

	for(Increment = n/2; Increment > 0; Increment /= 2)		//根据希尔增量的从ht = N/2缩小,h(k) = h(k+1)/2,一直到h1=1,逐步对数组进行t趟排序
	{
		for(i = Increment; i < n; i ++)		//分别对数组分割成t份的子数组进行插入排序操作,使得A[i]<=A[i+hk]恒成立
		{
			tmp = arr[i];
			for(j = i; j>=Increment; j -= Increment)	//轮流对t1,t2,t3……tk子数组执行直接插入排序
			{
				comparecount++;
				
				if(tmp < arr[j-Increment])
					arr[j] = arr[j-Increment];
				else
					break;
			}
			arr[j] = tmp;
			
			swapcount++;
		}
	}
}

//使用HibBard增量的希尔排序
void ShellSortByHibBardIncrement(int arr[], int n)
{
	int i, j;
	int Increment;		//HibBard增量
	int tmp;			//保存当前比较趟次位置i上的元素值

	comparecount = 0;
	swapcount = 0;

//log()       头文件:math.h
//功能:计算以e 为底的对数值。
//备注:计算任意数X为底的对数,可以利用对数的换底性质。
//如计算 logx(N) = loge(N)/loge(x) (x>0,N>0) 且x!=1 


	for(Increment = (int)log((float)(n+1))/log((float)2); Increment > 0; Increment = (Increment-1)/2)		//根据希尔增量的从ht = log2(n+1)的整数部分 缩小,h(k) = (h(k+1)-1)/2,一直到h1=1,逐步对数组进行t趟排序
	{
		for(i = Increment; i < n; i ++)		//分别对数组分割成t份的子数组进行插入排序操作,使得A[i]<=A[i+hk]恒成立
		{
			tmp = arr[i];
			for(j = i; j>=Increment; j -= Increment)	//轮流对t1,t2,t3……tk子数组执行直接插入排序
			{
				comparecount++;
				
				if(tmp < arr[j-Increment])
					arr[j] = arr[j-Increment];
				else
					break;
			}
			arr[j] = tmp;
			
			swapcount++;
		}
	}
}

//使用Knuth增量的希尔排序
void ShellSortByKnuthIncrement(int arr[], int n)
{
	int i, j;
	int Increment;		//Knuth增量
	int tmp;			//保存当前比较趟次位置i上的元素值

	comparecount = 0;
	swapcount = 0;


	long IncSeq_Knuth[10];			//Knuth增量序列

	IncSeq_Knuth[0] = 1;
	for(int s=1;s<=9;s++)
        IncSeq_Knuth[s]=IncSeq_Knuth[s-1]*3+1;
	
	int k = 9;
	while( n < IncSeq_Knuth[k] )
			k--;

	for( Increment = (int)IncSeq_Knuth[k]; Increment>0; Increment = (Increment-1)/3 )		//根据希尔增量的从小于n的最大knuth增量开始缩小,h(k) = (h(k+1)-1)/3,一直到h1=1,逐步对数组进行t趟排序
	{
		for(i = Increment; i < n; i ++)		//分别对数组分割成t份的子数组进行插入排序操作,使得A[i]<=A[i+hk]恒成立
		{
			tmp = arr[i];
			for(j = i; j>=Increment; j -= Increment)	//轮流对t1,t2,t3……tk子数组执行直接插入排序
			{
				comparecount++;
				
				if(tmp < arr[j-Increment])
					arr[j] = arr[j-Increment];
				else
					break;
			}
			arr[j] = tmp;
			
			swapcount++;
		}
	}

	/*运行报错: Stack around the variable 'IncSeq_Knuth' was corrupted      变量“IncSeq_Knuth”周围的堆栈已损坏   解决方案:把 project->配置属性->c/c++->代码生成->基本运行时检查 为 默认值 就不会报本异常。具体原因正在研究中。。。    
	如果改为其他就有exception。    
	exception有时是有道理的  
	https://www.cnblogs.com/flysnail/archive/2011/09/21/2184114.html
	把“project->配置属性->c/c++->代码生成->基本运行时检查 设置为默认值,就没有这样的错误了。关于MSDN的解释是在堆栈外面读写某数据。错误是名为RTC1的编译器检测的。
	最终解决方案:是程序中的一个字符数组大小少算了一个“\0”。把发生错误的那个数组空间+1就ok了。其实是之前IncSeq_Knuth数组循环赋值的时候k=10,然而 IncSeq_Knuth[10] 越界了
	*/
}

//使用Sedgewick增量的希尔排序
void ShellSortBySedgewickIncrement(int arr[], int n)
{
	int i, j;
	int Increment;		//Sedgewick增量
	int tmp;			//保存当前比较趟次位置i上的元素值

	comparecount = 0;
	swapcount = 0;

	//Sedgewick的通项公式和递推公式暂时不知如何得知。所以先用具体增量值数组
	int IncrementSequence_Sedgewick[28] = {1,5,19,41,109,209,505,929,
    2161,3905,8929,16001,36289,64769,146305,260609,
    587521,1045505,2354689,4188161,9427969,16764929,37730305,67084289,
	150958081,268386305,603906049,1073643521};			//Sedgewick增量序列           int型整数最大值为 2147483647 ,所以不会出现溢出
	
	int k = 27;
	while( n < IncrementSequence_Sedgewick[k] && k>=0)
			k--;

	for( Increment = IncrementSequence_Sedgewick[k]; k >= 0; Increment = IncrementSequence_Sedgewick[--k])		//根据希尔增量的从小于n的最大Sedgewick增量缩小,h(k) = IncrementSequence_Sedgewick[k],
	{																											//一直到h1=1,逐步对数组进行t趟排序
		for(i = Increment; i < n; i ++)		//分别对数组分割成t份的子数组进行插入排序操作,使得A[i]<=A[i+hk]恒成立
		{
			tmp = arr[i];
			for(j = i; j>=Increment; j -= Increment)	//轮流对t1,t2,t3……tk子数组执行直接插入排序
			{
				comparecount++;
							
				if(tmp < arr[j-Increment])
					arr[j] = arr[j-Increment];
				else
					break;
			}
			arr[j] = tmp;
			
			swapcount++;
		}
	}
}


//归并排序(自顶向下,自底向上)				
/*
将两个的有序数列合并成一个有序数列,我们称之为"归并"。 若将两个有序表合并成一个有序表,称为二路归并。         ------核心思想“分治”
归并排序(Merge Sort)就是利用归并思想对数列进行排序。根据具体的实现,归并排序包括"从上往下"和"从下往上"2种方式。
1. 从下往上(自底向上)的归并排序:自底向上的思想就是,进行分解,直接从线性表中的单个元素开始,进行两两合并,然后再以每两个元素为单位,进行两两合并……直到最后只剩下一个线性表。     	---------用迭代实现
将待排序的数列分成若干个长度为1的子数列,然后将这些数列两两合并;得到若干个长度为2的有序数列,再将这些数列两两 合并;
得到若干个长度为4的有序数列,再将它们两两合并;直接合并成一个数列为止。这样就得到了我们想要的排序结果。

2. 从上往下(自顶向下)的归并排序:它与"从下往上"在排序上是反方向的。它基本包括3步:	---------用递归实现
① 分解 -- 将当前区间一分为二,即求分裂点 mid = (low + high)/2; 
② 求解 -- 递归地对两个子区间a[low...mid] 和 a[mid+1...high]进行归并排序。递归的终结条件是子区间长度为1。
③ 合并 -- 将已排序的两个子区间a[low...mid]和 a[mid+1...high]归并为一个有序的区间a[low...high]。

归并(合并)过程为:比较a[i]和b[j]的大小,若a[i]≤b[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;
否则将第二个有序表中的元素b[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。
*/

//合并算法
void Merge(int arr[], int Temp[], int Lpos, int Rpos, int Rend)
{
	int Lend = Rpos -1;			//左半边有序区间结束位置
	int NumElement;				//合并后元素个数
	int TmpPos = Lpos;			//合并后临时数组起始下标
	//int TmpPos = 0;			//合并后临时数组起始下标
	
	NumElement = Rend - Lpos + 1;

	//合并循环
	while(Lpos <= Lend && Rpos <= Rend)
	{
		if(arr[Lpos] <= arr[Rpos])
			Temp[TmpPos++] = arr[Lpos++];
		else
			Temp[TmpPos++] = arr[Rpos++];
	}

	//左半边剩下的直接排后边
	while(Lpos <= Lend)
		Temp[TmpPos++] = arr[Lpos++];
	//左半边剩下的直接排后边
	while(Rpos <= Rend)
		Temp[TmpPos++] = arr[Rpos++];

	//从临时数组拷贝合并后数据回原数组
	for(int i = 0; i<NumElement; i++,Rend--)		//为啥不能像Merge1函数那样写?????????
		arr[Rend] = Temp[Rend];

	//Print(arr,n);
}

void Merge1(int sourceArr[],int tempArr[], int startIndex, int midIndex, int endIndex)
{
    int i = startIndex, j=midIndex+1, k = startIndex;
    while(i<=midIndex && j<=endIndex)
    {
        if(sourceArr[i] > sourceArr[j])
            tempArr[k++] = sourceArr[j++];
        else
            tempArr[k++] = sourceArr[i++];
    }
    while(i <= midIndex)
        tempArr[k++] = sourceArr[i++];
    while(j <= endIndex)
        tempArr[k++] = sourceArr[j++];
    for(i=startIndex; i<=endIndex; i++)
        sourceArr[i] = tempArr[i];
}

void TopDownMSort(int arr[], int Temp[], int left, int right)
{
	int mid;

	if(left < right)
	{
		mid = left + (right-left)/2;		//用减法不用加法,避免int溢出
		//printf("mid = %d\n",mid);	

		//PFL;
		TopDownMSort(arr,Temp,left,mid);
		//PFL;
		TopDownMSort(arr,Temp,mid+1,right);
		//PFL;
		Merge(arr,Temp,left,mid+1,right);
		//Merge1(arr,Temp,left,mid,right);
	}
	executecount++;
}
//递归实现归并排序-------自顶向下
/*
自底向上的归并排序并未用到递归的思想,而是单纯地使用了迭代,在算法效率上是和之前讲到的自顶向下的归并排序差不多的,但是在代码全程没有用到对数组下标的引用,说明这个算法是可以用在对链表的排序上的。
*/
void MergeSortByRecursion(int arr[], int n)			
{
	executecount = 0;

	int *Temp = (int*)malloc(sizeof(int)*n);  
	if(Temp != NULL)
	{
		TopDownMSort(arr,Temp,0,n-1);
		free(Temp);
	}
	else
		perror("Out of space");
}

void merge(int A[],int tempArry[],int lo,int mid,int hi)
{
	int i = lo, j = mid + 1;

	for(int k=lo;k<=hi;k++)
	{
		tempArry[k] = A[k];
	}

	for(int k=lo;k<=hi;k++)
	{
		if (i > mid)
			A[k] = tempArry[j++];
		else if (j > hi)
			A[k] = tempArry[i++];
		else if (tempArry[j] > tempArry[i])
			A[k] = tempArry[i++];
		else
			A[k] = tempArry[j++];
	
		executecount++;
	}
}
void BottomUpMSort(int arr[], int Temp[], int n)
{
	for(int i = 1; i<n; i *=2)		//以从i=1的长度开始合并两个有序表,合并长度以两倍递增
		for(int j=0; j<n-i; j += 2*i)
		{
			merge(arr,Temp, j, i+j-1, min(j + 2*i - 1, n-1));
		}
}
//迭代实现归并排序-------自底向上
//首先最外层循环需要对归并的个数进行遍历,size从1开始遍历到n,每次循环增加自身值,即(1->2->4->8)
//内存循环就是每一轮在归并过程起始的元素位置,位置从0开始到n - sz,每次循环增加2个size,即第一轮从0~size-1、从size~2size-1这两部分进行归并,第二轮从2size~3size-1、从3size~4size-1这两部分进行归并。
//注意:这里代码编写需要严谨考虑越界问题。
//最后一次归并的第二个子数组可能比第一个子数组要小
void MergeSortByIteration(int arr[], int n)
{
	executecount = 0;

	int *Temp = (int*)malloc(sizeof(int)*n);  
	if(Temp != NULL)
	{
		BottomUpMSort(arr,Temp,n);
		free(Temp);
	}
	else
		perror("Out of space!!");
}


const int Cutoff = 3;
enum PivotType
{
	First = 0,
	Random,
	Median3
};
int t_PivotType;
enum QuickSortType
{
	General = 0,
	TwoWay,
	ThreeWay
};
int t_QuickSortType;

//快速排序(快速排序,二路快排,三路快排)
/*
顾名思义,快速排序是在实践中最快的已知排序算法,它的平均运行时间是O(NlogN),最坏情形的性能为O(N^2)
像归并排序一样,快速排序也是一种分治的递归算法。将数组S排序的基本算法由下列简单的四步组成:
1.如果S中元素个数是0或1,则返回。
2.取S中任意元素v,称之为枢纽元(pivot)。
3.将S-{v}(S中其余元素)分成两个不想交的集合:S₁={x∈S-{v} | x≦v}和S₂={x∈S-{v} | x≧v}。
4.返回{quicksort(S₁)后,继而v,继而quicksort(S₂)}。

关于快排的三种排序详解见:https://blog.csdn.net/k_koris/article/details/80585979
*/
//选取第一个元素用作枢纽元
int SelectTheFirstAsPivot(int arr[], int Left, int Right)
{
	//Hide pivot
	swap(arr[Left], arr[Right]);
	//Print(arr,Right-Left+1);

	return arr[Right];
}
//随机选取枢纽元
int SelectRandomElementAsPivot(int arr[], int Left, int Right)
{
	srand(time(NULL));

	int RandonIndex = randint(Left,Right);

	//printf("RandomIndex = %d,arr[RandonIndex] = %d\n", RandonIndex, arr[RandonIndex]);
	swap(arr[RandonIndex], arr[Right]);

	return arr[Right];
}
//使用左端、右端和中心位置上的三个元素的中值作为枢纽元
//Return median of left,center,and right order these and hide the pivot	返回左、中、右的中间值按顺序排列并隐藏轴
int SelectMedianOfTHreePartioningAsPivot(int arr[], int Left, int Right)
{
	int Center = Left+(Right-Left)/2;

	if(arr[Left] > arr[Center])
		swap(arr[Left], arr[Center]);
	if(arr[Left] > arr[Right])
		swap(arr[Left], arr[Right]);
	if(arr[Center] > arr[Right])
		swap(arr[Center], arr[Right]);

	//Hide pivot
	swap(arr[Center], arr[Right-1]);
	return arr[Right-1];
}

//普通快排
void Qsort(int arr[], int Left, int Right)
{
	int i,j;
	int Pivot;		//枢纽元

	if(t_PivotType == First)
	{
		if(Left + 1<= Right)
		{
			Pivot = SelectTheFirstAsPivot(arr, Left, Right);
			j = Left-1;		//小于Pivot元素组成的区间的右边界,其左边界为Left,即[Left,j]。刚开始没有元素,所以j初始化为 Left-1

			for( i = Left; i < Right; i++)	//遍历区间[0,Right-1]内的元素。-------------大于Pivot元素组成的区间为[j+1,i-1]。
			{
				if(arr[i] < Pivot)
				{
					swap(arr[j+1],arr[i]);
					j++;
				}

				//executecount++;
			}

			swap(arr[Right], arr[j+1]);			//复原枢纽元位置
			Qsort(arr, Left, j);
			Qsort(arr, j+2, Right);
		}

		executecount++;
	}
	else if(t_PivotType == Random)
	{
		if(Left + 1 <= Right)
		{
			Pivot = SelectRandomElementAsPivot(arr, Left, Right);
			j = Left-1;		//小于Pivot元素组成的区间的右边界,其左边界为Left,即[Left,j]。刚开始没有元素,所以j初始化为 Left-1

			for( i = Left; i < Right; i++)	//遍历区间[0,Right-1]内的元素。-------------大于Pivot元素组成的区间为[j+1,i-1]。
			{
				if(arr[i] < Pivot)
				{
					swap(arr[j+1],arr[i]);
					j++;
				}

				//executecount++;
			}

			swap(arr[Right], arr[j+1]);			//复原枢纽元位置
			Qsort(arr, Left, j);
			Qsort(arr, j+2, Right);
		}

		executecount++;
	}
	else
	{	
		assert(t_PivotType == Median3);

		if(Left + Cutoff <= Right)
		{
			Pivot = SelectMedianOfTHreePartioningAsPivot(arr, Left, Right);
			j = Left-1;		//小于Pivot元素组成的区间的右边界,其左边界为Left,即[Left,j]。

			for( i = Left; i < Right-1; i++)	//遍历区间[0,Right-2]内的元素,(选取三素中值做枢纽元时已经将大于 Pivot 的一个元素放到 Right 位置和将 Pivot 元素放到 Right-1 位置)。-------------大于Pivot元素组成的区间为[j+1,i-1]。
			{
				if(arr[i] < Pivot)
				{
					swap(arr[j+1],arr[i]);
					j++;
				}

				//executecount++;
			}

			swap(arr[Right-1], arr[j+1]);			//复原枢纽元位置
			Qsort(arr, Left, j);
			Qsort(arr, j+2, Right);
		}
		else
			InsertSort(arr+Left, Right-Left+1);

		executecount++;
	}

}

//两路快排
void QsortByTwoWays(int arr[], int Left, int Right)
{
	int i,j;
	int Pivot;		//枢纽元

	if(t_PivotType == First)
	{
		if(Left + 1 <= Right)
		{
			Pivot = SelectTheFirstAsPivot(arr, Left, Right);
			i = Left-1;		//因为循环前先自增1,所以初始值为 Left-1
			j = Right;		//因为循环前先自减1,而枢纽元保存在数组最后一个位置,所以初始值为 Right

			for( ; ; )	//Partition 分区操作 小于Pivot的放到左边,大于Pivot的放到右边
			{
				while (Pivot > arr[++i]) { }
				while (Pivot < arr[--j]) { }

				if(i < j)
					swap(arr[i], arr[j]);
				else
					break;

				//executecount++;
			}

			swap(arr[Right], arr[i]);			//复原枢纽元位置

			QsortByTwoWays(arr, Left, i-1);
			QsortByTwoWays(arr, i+1, Right);
		}

		executecount++;
	}
	else if(t_PivotType == Random)
	{
		if(Left + 1 <= Right)
		{
			Pivot = SelectRandomElementAsPivot(arr, Left, Right);
			i = Left-1;
			j = Right;

			for( ; ; )	//Partition 分区操作 小于Pivot的放到左边,大于Pivot的放到右边
			{
				while (Pivot > arr[++i]) { }
				while (Pivot < arr[--j]) { }

				if(i < j)
					swap(arr[i], arr[j]);
				else
					break;

				//executecount++;
			}

			swap(arr[Right], arr[i]);			//复原枢纽元位置
			
			//Print(arr,Right-Left+1);

			QsortByTwoWays(arr, Left, i-1);
			QsortByTwoWays(arr, i+1, Right);
		}

		executecount++;
	}
	else
	{
		assert(t_PivotType == Median3);

		if(Left + Cutoff <= Right)
		{
			Pivot = SelectMedianOfTHreePartioningAsPivot(arr, Left, Right);
			i = Left;
			j = Right-1;
			for( ; ; )		//Partition 分区操作 将小于Pivot的元素都移到左边,将大于Pivot的元素都移到右边
			{
				while(arr[++i] < Pivot){}
				while(arr[--j] > Pivot){}

				if(i<j)
					swap(arr[i], arr[j]);
				else
					break;

				//executecount++;
			}
			swap(arr[i], arr[Right-1]);		//还原枢纽元

			QsortByTwoWays(arr, Left, i-1);
			QsortByTwoWays(arr, i+1, Right);
		}
		else
			InsertSort(arr+Left, Right-Left+1);

		executecount++;
	}

}

//三路快排
void QsortByThreeWays(int arr[], int Left, int Right)
{
	int lt,gt;			
	int Pivot;		//枢纽元
	int ergodic;	//当前遍历位置

	if(t_PivotType == First)
	{
		if(Left + 1 <= Right)
		{
			//选取枢纽元
			//Pivot = SelectTheFirstAsPivot(arr, Left, Right);
			Pivot = arr[Left];		//三路快排不需要隐藏枢纽元Pivot

			ergodic = Left+1;		//当前遍历位置,因为选取第一个元素做Pivot,所以可直接从第二个元素开始遍历
			lt = Left-1;			//lt(less than)表示小于枢纽元Pivot的区间右边界 [Left,lt];同时等于枢纽元Pivot的区间为[lt+1,ergodic-1]
			gt = Right+1;			//gt(greater than)表示大于枢纽元Pivot的区间左边界 [gt,Right]

			while(ergodic < gt)	//Partition 分区操作 小于Pivot的放到左边,大于Pivot的放到右边,等于Pivot的位置不变。
			{
				if(arr[ergodic] < Pivot)
				{
					swap(arr[lt+1], arr[ergodic]);
					lt++;
					ergodic++;
				}
				else if(arr[ergodic] > Pivot)
				{
					swap(arr[ergodic], arr[gt-1]);
					gt--;
				}
				else
					ergodic++;

				//executecount++;
			}

			//swap(arr[Right], arr[i]);			//复原枢纽元位置

			QsortByThreeWays(arr, Left, lt);
			QsortByThreeWays(arr, gt, Right);
		}

		executecount++;
	}
	else if(t_PivotType == Random)
	{
		if(Left + 1 <= Right)
		{
			//选取枢纽元
			Pivot = SelectRandomElementAsPivot(arr, Left, Right);

			ergodic = Left;		//当前遍历位置,因为选取第一个元素做Pivot,所以可直接从第二个元素开始遍历
			lt = Left-1;			//lt(less than)表示小于枢纽元Pivot的区间右边界 [Left,lt];同时等于枢纽元Pivot的区间为[lt+1,ergodic-1]
			gt = Right;				//gt(greater than)表示大于枢纽元Pivot的区间左边界 [gt,Right-1],因为选取枢纽元的时候已经将 Pivot 元素放到 Right 位置

			while(ergodic < gt)	//Partition 分区操作 小于Pivot的放到左边,大于Pivot的放到右边,等于Pivot的位置不变。
			{
				if(arr[ergodic] < Pivot)
				{
					swap(arr[lt+1], arr[ergodic]);
					lt++;
					ergodic++;
				}
				else if(arr[ergodic] > Pivot)
				{
					swap(arr[ergodic], arr[gt-1]);
					gt--;
				}
				else
					ergodic++;

				//executecount++;
			}		
			//循环结束后,ergodic == gt,且都指向大于Pivot区间的第一个元素位置

			swap(arr[Right], arr[ergodic]);			//复原枢纽元位置,此时大于Pivot的区间为[gt+1,Right]

			QsortByThreeWays(arr, Left, lt);
			QsortByThreeWays(arr, gt+1, Right);
		}

		executecount++;
	}
	else
	{
		assert(t_PivotType == Median3);

		if(Left + Cutoff <= Right)
		{
			Pivot = SelectMedianOfTHreePartioningAsPivot(arr, Left, Right);

			ergodic = Left+1;		//当前遍历位置,刚开始已经有一个确认小于Pivot的元素(选取三素中值做枢纽元时已经将小于 Pivot 的一个元素放到 Left 位置),所以从第二个元素开始遍历
			lt = Left;				//lt(less than)表示小于枢纽元Pivot的区间右边界 [Left,lt],因为已经有一个小于Pivot的元素放在Left位置,所以小于Pivot的初始区间为[Left,Left];同时等于枢纽元Pivot的区间为[lt+1,ergodic-1]
			gt = Right-1;			//gt(greater than)表示大于枢纽元Pivot的区间左边界 [gt,Right-2],选取三素中值做枢纽元时已经将大于 Pivot 的一个元素放到 Right 位置和将 Pivot 元素放到 Right-1 位置

			while(ergodic < gt)	//Partition 分区操作 小于Pivot的放到左边,大于Pivot的放到右边,等于Pivot的位置不变。
			{
				if(arr[ergodic] < Pivot)
				{
					swap(arr[lt+1], arr[ergodic]);
					lt++;
					ergodic++;
				}
				else if(arr[ergodic] > Pivot)
				{
					swap(arr[ergodic], arr[gt-1]);
					gt--;
				}
				else
					ergodic++;

				//executecount++;
			}		
			//循环结束后,ergodic == gt,且都指向大于Pivot区间的第一个元素位置

			swap(arr[Right-1], arr[ergodic]);			//复原枢纽元位置,此时大于Pivot的区间为[gt+1,Right]

			QsortByThreeWays(arr, Left, lt);
			QsortByThreeWays(arr, gt+1, Right);

		}
		else
			InsertSort(arr+Left, Right-Left+1);

		executecount++;
	}

}

//快速排序驱动例程
void QuickSort(int arr[], int n)
{
	executecount = 0;

	if(t_QuickSortType == General)
		Qsort(arr,0,n-1);
	else if(t_QuickSortType == TwoWay)
		QsortByTwoWays(arr,0,n-1);
	else
	{
		assert(t_QuickSortType == ThreeWay);
		QsortByThreeWays(arr,0,n-1);
	}
}


//堆排序
/*
堆排序是利用堆(当不加修饰地使用“堆”这个词时一般都是指二叉堆)这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。

堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。

二叉堆的两个性质:
结构性:以数组下标从0开始存储,对于数组中任意位置 i 上的元素,其左儿子在位置 (2i+1) 上,右二子在左儿子后的单元 (2i+2) 中,它的父亲则在位置 ( (i-1)/2 ) 上。
堆序性:在一个大顶堆中,对于每一个节点X,X的父亲中的关键字大于(或等于)X中的关键字,根节点除外(他没有父亲);在一个小顶堆中,对于每一个节点X,X的父亲中的关键字小于(或等于)X中的关键字,根节点除外(他没有父亲)。

堆排序的基本思想:
	将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。
	然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
基本步骤:
	步骤一 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。
	步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。
再简单总结下堆排序的基本思路:
  a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
  b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
  c.重新调整堆结构,使其满足堆的两个性质(结构性和堆序性),然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

————————————————
构建堆(BuildHeap)一般的算法是将N个关键字以任意顺序放入树中,保持结构特性。此时如果PercolateDown(i)从节点i下滤,那么执行下面的算法创建一个具有堆序的树(heap-ordered tree)。
for( i = N/2; i > 0; i--)
	PercolateDown(i);

————————————————
堆排序中的上滤和下滤
这两个地方搞了好多次,每次做题的时候都容易忘记,现在把其特点记下来。
	首先是下滤,就是把当前节点向下寻找其应该在的位置,它要求当前节点的左右子节点都满足堆的性质。最多的应用在堆排序的过程中:每次把最大堆的第一个节点(最大)和当前排序队列的最后一个元素交换,
这样当前最大值就去了该去的地方,再对当前首元素进行下滤。其次还有删除节点(就是循环的时候自变量--)。
	其次是上滤,这就是把当前节点向上寻找其该在的位置。重点是明白它的要求是必须保证当前节点以及是个最大(小)堆。要不然,如果当前节点和这个节点的全部子节点形成的队列不是个最大(小)堆得话,上滤是没有意义的,
因为并没有把真正最大(小)的元素“滤”上去。它最多的应用是插入节点。

构造堆得过程有2种方式。
1,循环每一个无序元素,把它插入到最大堆得末尾然后上滤。
2,直接从最后一个非叶子节点的元素开始往根节点下滤。

这里第一种方式没啥可以说的。主要是第二种。我也不知道问什么每次做题都要一愣,甚至好几次没做出来。看来还是理解不深。
首先为什么要从倒数第一个非叶子节点开始。首先对于叶子节点,明显下滤是没有意义的,因为没有子节点下滤当然还是不变。其次解释为什么要从后往前。这里要真的明白下滤的意义。
下滤的要求是当前节点的左节点和右节点都已经是最大堆了。那么如果从根节点开始下滤,它怎么保证这一点呢?所以必须从第一个有意义的节点(也就是倒数第一个非叶子节点)开始,
从后往前下滤,每次下滤的时候因为前面下滤过了,可以保证当前下滤的时候,当前节点的左右子节点都是最大堆。到最后根节点当然就让整个队列都变成最大堆了。

————————————————
在学习到堆排序时,对于上滤和下滤的知识掌握的不清楚,回来复习了一下。有了些感悟,记下来。

上滤(percolate up) 
上滤一般应用于在一个已经排序好的二叉堆中插入一个新节点。通过上滤,使堆在容纳了新节点后仍能保持原来的堆序。 
上滤的主要思想: 
首先在堆末新建一个空间,称为空穴(hole),然后比较穴的值和其父节点的值。如果空穴的当前位置满足整个堆的堆序,那么就把要插入的值赋给空穴;否则,交换空穴和父节点的位置。
迭代实现上面的过程,直到符合整个堆的堆序为止。从宏观上看,空穴会自下而上 地到达满足堆序的位置,称为上滤(percolate up)。

下滤(percolate down) 
下滤一般应用于删除了堆顶后的堆序重整过程中。通过下滤,使堆在删除堆顶后把新的堆顶放置在满足堆序的正确的位置上。
同时,亦可应用于对一个无序数组的堆排序的算法中。把数组视为一个无序的堆,通过下滤,使堆重新满足一定的堆序。 

下滤一般针对堆中的某一个位置进行,传入的参数一般为三个:数组的引用&v、下滤的位置i、数组的大小n(也可以重载为4个参数,添加一个比较器来确定下滤为最大堆还是最小堆)。
假设下滤为最小堆,首先寻找该节点的左右两个孩子中最小的孩子,然后比较该节点和最小的孩子,如果孩子比较小,就交换二者的位置。迭代进行这个过程,直到该节点为叶子节点为止。
从宏观上看,节点会在以该节点为根的堆中,自上而下地到达其满足整个堆序的正确位置,称为下滤(percolate down)。 
在删除堆顶后的堆序重整过程中,堆末的元素会放入堆顶,而其他元素位置不变,从而保证了所有真子堆满足堆序,所以只需对堆顶执行下滤即可。 

在堆排序的过程中,由于不是所有元素都按照堆序排列,因此需要对每一个非叶子节点进行下滤。节点进行下滤的顺序是,从堆中最后一个非叶子节点开始,到堆顶结束。
这样可以保证对于每个即将进行下滤的节点,以他们为根的子堆都是满足堆序的,因此对该节点下滤后以该节点为根的堆可以得到正确的堆序。
(为什么不从叶子节点开始下滤?因为下滤过程不会对叶子节点进行,所以没有必要从叶子节点开始,只需从n / 2的位置开始就可以了)

最后总结一下构造堆的方法 
对于一个无序数组,构造一个堆的方法有两种: 
1. 建立一个数组,遍历无序数组的每个元素,依次对新数组执行堆的插入操作,每次插入都执行一次上滤的过程。 
2. 直接从数组中抽象的最后一个非叶子节点开始,对经过的每个节点执行一次下滤的过程,直到抽象的堆顶结束。
————————————————
*/

//调整大顶堆,满足堆序性-------------因为最终是要以升序排序,所以创建一个大顶堆(Max-Heap)
//输入参数:arr[] 数组指针,i 下滤的位置,n 堆的大小

#define LeftChild(i) (2*(i) + 1)
void AdjustMaxHeap(int arr[], int i, int n)
{
	int Child;		//儿子节点
	int Tmp;		//临时变量

	Tmp = arr[i];	//保存当前下滤节点值
	for(Child = LeftChild(i); Child < n; Child = LeftChild(Child))
	{	
		//查找更大的儿子节点 -----------先判断边界(是否有右节点),后比较两子节点大小
		if(Child+1 < n && arr[Child+1] > arr[Child])
			Child++;

		//如果子节点大于父节点,将子节点值赋给父节点(先不做交换)
		if(arr[Child] > Tmp)
		{
			arr[i] = arr[Child];
			i = Child;		//找到满足下滤的子节点后更新Tmp置入节点的位置
		}
		else
			break;
	}
	arr[i] = Tmp;		//将Tmp值放到最终的位置

	executecount++;
}

void HeapSort(int arr[], int n)
{
	executecount = 0;

	//构建大顶堆
	for(int i = n/2-1; i >= 0; i--)
		AdjustMaxHeap(arr, i, n);

	//将堆顶(总是数组第一个元素)元素与堆最后一个元素交换,之后以除堆顶元素之外(此时其实就是删除了堆顶元素)的其它元素做堆调整,使其保持堆序性
	for(int i = n-1; i > 0; i--)
	{
		swap(arr[0], arr[i]);		//将堆顶元素与末尾元素进行交换
		AdjustMaxHeap(arr, 0, i);	//重新对堆进行调整,其中参数i的递减,就是删除对换后处于末尾的堆顶元素,缩小堆的大小。
	}
}


///比较排序

//非比较排序///
//桶排序


//基数排序


//计数排序


//非比较排序///

int main()
{
	int *P,*Q;
	int arrBubbleSort1[n],arrBubbleSort2[n],arrSelectionSort[n],arrInsertSort[n],arrBinaryInsertionSort[n];
	int arrShellSortByHillIncrement[n],arrShellSortByHibBardIncrement[n],arrShellSortByKnuthIncrement[n],arrShellSortBySedgewickIncrement[n];
	int arrMergeSortByRecursion[n],arrMergeSortByIteration[n];
	int arrQuickSortByFirst[n],arrQuickSortByRandom[n],arrQuickSortByMedian3[n];
	int arrQuickSortTwoWaysByFirst[n],arrQuickSortTwoWaysByRandom[n],arrQuickSortTwoWaysByMedian3[n];
	int arrQuickSortThreeWaysByFirst[n],arrQuickSortThreeWaysByRandom[n],arrQuickSortThreeWaysByMedian3[n];
	int arrHeapSort[n];



	P = generateRandomArray(n,1,MaxRandom);
	Q = P;


	for(int i=0; i<n; i++)
	{
		arrBubbleSort1[i] = *Q++;
	}

	
	for(int i=0; i<n; i++)
	{
		arrBubbleSort2[i] = arrBubbleSort1[i];
	}

	for(int i=0; i<n; i++)
	{
		arrSelectionSort[i] = arrBubbleSort1[i];
	}

	for(int i=0; i<n; i++)
	{
		arrInsertSort[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrBinaryInsertionSort[i] = arrBubbleSort1[i];
	}

	for(int i=0; i<n; i++)
	{
		arrShellSortByHillIncrement[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrShellSortByHibBardIncrement[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrShellSortByKnuthIncrement[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrShellSortBySedgewickIncrement[i] = arrBubbleSort1[i];
	}

	for(int i=0; i<n; i++)
	{
		arrMergeSortByRecursion[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrMergeSortByIteration[i] = arrBubbleSort1[i];
	}
	

	for(int i=0; i<n; i++)
	{
		arrQuickSortByFirst[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrQuickSortByRandom[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrQuickSortByMedian3[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrQuickSortTwoWaysByFirst[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrQuickSortTwoWaysByRandom[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrQuickSortTwoWaysByMedian3[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrQuickSortThreeWaysByFirst[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrQuickSortThreeWaysByRandom[i] = arrBubbleSort1[i];
	}
	for(int i=0; i<n; i++)
	{
		arrQuickSortThreeWaysByMedian3[i] = arrBubbleSort1[i];
	}

	for(int i=0; i<n; i++)
	{
		arrHeapSort[i] = arrBubbleSort1[i];
	}


	Print(P,n);
	BubbleSort(P,n);
	cout<<"My BubbleSort:"<<endl;
	Print(P,n);
	cout<<"comparecount:"<<comparecount<<endl;
	cout<<"swapcount:"<<swapcount<<endl;


	Print(arrBubbleSort1,n);
	BubbleSort1(arrBubbleSort1,n);
	cout<<"别人优化的 BubbleSort1:"<<endl;
	Print(arrBubbleSort1,n);
	cout<<"comparecount:"<<comparecount<<endl;
	cout<<"swapcount:"<<swapcount<<endl;


	Print(arrBubbleSort2,n);
	BubbleSort2(arrBubbleSort2,n);
	cout<<"普通的 BubbleSort2:"<<endl;
	Print(arrBubbleSort2,n);
	cout<<"comparecount:"<<comparecount<<endl;
	cout<<"swapcount:"<<swapcount<<endl;

	
	Print(arrSelectionSort,n);
	SelectionSort(arrSelectionSort,n);
	cout<<"My SelectionSort:"<<endl;
	Print(arrSelectionSort,n);
	cout<<"comparecount:"<<comparecount<<endl;
	cout<<"swapcount:"<<swapcount<<endl;


	Print(arrInsertSort,n);
	InsertSort(arrInsertSort,n);
	cout<<"My InsertionSort:"<<endl;
	Print(arrInsertSort,n);
	cout<<"comparecount:"<<comparecount<<endl;
	cout<<"swapcount:"<<swapcount<<endl;


	Print(arrBinaryInsertionSort,n);
	BinaryInsertionSort(arrBinaryInsertionSort,n);
	cout<<"My arrBinaryInsertionSort:"<<endl;
	Print(arrBinaryInsertionSort,n);
	cout<<"comparecount:"<<comparecount<<endl;
	cout<<"swapcount:"<<swapcount<<endl;


	Print(arrShellSortByHillIncrement,n);
	ShellSortByHillIncrement(arrShellSortByHillIncrement,n);
	cout<<"My arrShellSortByHillIncrement:"<<endl;
	Print(arrShellSortByHillIncrement,n);
	cout<<"comparecount:"<<comparecount<<endl;
	cout<<"swapcount:"<<swapcount<<endl;


	Print(arrShellSortByHibBardIncrement,n);
	ShellSortByHibBardIncrement(arrShellSortByHibBardIncrement,n);
	cout<<"My arrShellSortByHibBardIncrement:"<<endl;
	Print(arrShellSortByHibBardIncrement,n);
	cout<<"comparecount:"<<comparecount<<endl;
	cout<<"swapcount:"<<swapcount<<endl;


	Print(arrShellSortByKnuthIncrement,n);
	ShellSortByKnuthIncrement(arrShellSortByKnuthIncrement,n);
	cout<<"My arrShellSortByKnuthIncrement:"<<endl;
	Print(arrShellSortByKnuthIncrement,n);
	cout<<"comparecount:"<<comparecount<<endl;
	cout<<"swapcount:"<<swapcount<<endl;

	
	Print(arrShellSortBySedgewickIncrement,n);
	ShellSortBySedgewickIncrement(arrShellSortBySedgewickIncrement,n);
	cout<<"My arrShellSortBySedgewickIncrement:"<<endl;
	Print(arrShellSortBySedgewickIncrement,n);
	cout<<"comparecount:"<<comparecount<<endl;
	cout<<"swapcount:"<<swapcount<<endl;


	Print(arrMergeSortByRecursion,n);
	MergeSortByRecursion(arrMergeSortByRecursion,n);
	cout<<"My arrMergeSortByRecursion:"<<endl;
	Print(arrMergeSortByRecursion,n);
	cout<<"executecount:"<<executecount<<endl;

	
	Print(arrMergeSortByIteration,n);
	MergeSortByIteration(arrMergeSortByIteration,n);
	cout<<"My arrMergeSortByIteration:"<<endl;
	Print(arrMergeSortByIteration,n);
	cout<<"executecount:"<<executecount<<endl;


	cout<<"Source Array:"<<endl;

	t_QuickSortType = General;
	t_PivotType = First;

	Print(arrQuickSortByFirst,n);
	QuickSort(arrQuickSortByFirst,n);
	cout<<"My arrQuickSortByFirst:"<<endl;
	Print(arrQuickSortByFirst,n);
	cout<<"executecount:"<<executecount<<endl;

	t_PivotType = Random;
	//Print(arrQuickSortByRandom,n);
	QuickSort(arrQuickSortByRandom,n);
	cout<<"My arrQuickSortByRandom:"<<endl;
	Print(arrQuickSortByRandom,n);
	cout<<"executecount:"<<executecount<<endl;

	t_PivotType = Median3;
	//Print(arrQuickSortByMedian3,n);
	QuickSort(arrQuickSortByMedian3,n);
	cout<<"My arrQuickSortByMedian3:"<<endl;
	Print(arrQuickSortByMedian3,n);
	cout<<"executecount:"<<executecount<<endl;


	t_QuickSortType = TwoWay;
	t_PivotType = First;

	//Print(arrQuickSortTwoWaysByFirst,n);
	QuickSort(arrQuickSortTwoWaysByFirst,n);
	cout<<"My arrQuickSortTwoWaysByFirst:"<<endl;
	Print(arrQuickSortTwoWaysByFirst,n);
	cout<<"executecount:"<<executecount<<endl;

	t_PivotType = Random;
	//Print(arrQuickSortTwoWaysByRandom,n);
	QuickSort(arrQuickSortTwoWaysByRandom,n);
	cout<<"My arrQuickSortTwoWaysByRandom:"<<endl;
	Print(arrQuickSortTwoWaysByRandom,n);
	cout<<"executecount:"<<executecount<<endl;

	t_PivotType = Median3;
	//Print(arrQuickSortTwoWaysByMedian3,n);
	QuickSort(arrQuickSortTwoWaysByMedian3,n);
	cout<<"My arrQuickSortTwoWaysByMedian3:"<<endl;
	Print(arrQuickSortTwoWaysByMedian3,n);
	cout<<"executecount:"<<executecount<<endl;

	t_QuickSortType = ThreeWay;
	t_PivotType = First;
	//Print(arrQuickSortThreeWaysByFirst,n);
	QuickSort(arrQuickSortThreeWaysByFirst,n);
	cout<<"My arrQuickSortThreeWaysByFirst:"<<endl;
	Print(arrQuickSortThreeWaysByFirst,n);
	cout<<"executecount:"<<executecount<<endl;

	t_PivotType = Random;
	//Print(arrQuickSortThreeWaysByRandom,n);
	QuickSort(arrQuickSortThreeWaysByRandom,n);
	cout<<"My arrQuickSortThreeWaysByRandom:"<<endl;
	Print(arrQuickSortThreeWaysByRandom,n);
	cout<<"executecount:"<<executecount<<endl;

	t_PivotType = Median3;
	//Print(arrQuickSortThreeWaysByMedian3,n);
	QuickSort(arrQuickSortThreeWaysByMedian3,n);
	cout<<"My arrQuickSortThreeWaysByMedian3:"<<endl;
	Print(arrQuickSortThreeWaysByMedian3,n);
	cout<<"executecount:"<<executecount<<endl;


	cout<<"Source Array:"<<endl;
	Print(arrHeapSort,n);
	HeapSort(arrHeapSort,n);
	cout<<"My arrHeapSort:"<<endl;
	Print(arrHeapSort,n);
	cout<<"executecount:"<<executecount<<endl;


	printf("\n");
	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值