【Data_Structure笔记14】【笔试】之【所有排序算法】

/***********************************************************************************************************************
文件说明:
        为面试准备的【内部排序算法】
开发环境:
        Win10+VS2012
时间地点:
        陕西师范大学 文津楼
作    者:
        九 月
***********************************************************************************************************************/
#include<iostream>

using namespace std;

#define ARRAY_LENTGTH 15

typedef int ElemType;

ElemType srcData[ARRAY_LENTGTH] = {10,9,8,7,6,5,4,3,2,1,1000,2000,900,300,200};

//===============================================================================================================================
//=====================================================【插入排序算法】==========================================================
//===============================================================================================================================
/***********************************************************************************************************************
函数原型:
        template<typename DataType>void InsertSort(DataType arrayT[],int iLength)
函数说明:
        【排序算法1】---------【直接插入排序】
性能分析:
        【1】【空间效率】:仅使用了数个辅助单元,因而空间复杂度为O(1);
		【2】【时间效率】:在排序过程中,向有序子表中逐个插入元素的操作进行了n-1趟,每趟的操作部分分为【比较关键字】和
		                 【移动元素】,而【比较次数】和【移动次数】取决于待排序表的【初始状态】。
		【3】【最好情况下】:表中的元素已经有序,此事每插入一个元素,都只需比较一次而不用移动元素,因而时间复杂度为O(n).
		【4】【最坏情况下】:表中的元素刚好和排序结果中的元素顺序相反【逆序】时,总的比较次数达到最大,总的移动次数也达
		                  到最大。
		【5】【平均情况下】:考虑待排序表中的元素是随机的,此时,可以去上述最好与最坏情况的平均值作为平均情况下的复杂度,
		                  总的比较次数和移动次数约为(n*n)/4
		【6】由此,【直接插入排序算法】的【时间复杂度】为O(n*n).虽然【折半插入算法】的【时间复杂度】也为O(n*n),但是对于
		                  【数据量比较小】的【排序表】,【折半插入排序】往往表现出更好的性能。
		【7】【使用性】:
		               【直接插入排序算法】适用于【顺序存储】和【链式存储】的【线性表】
***********************************************************************************************************************/
template<typename DataType>void InsertSort(DataType srcData[],int iLength)
{
	DataType   tempT;                                              //【1】定义一个哨兵
	int       j       = 0;                                        
	for(int i=0;i<iLength;i++)                                    //【2】n个数,进行n-1趟的插入
	{
		tempT = srcData[i];
		j     = i-1;
		while (tempT<srcData[j])                                  //【3】搜索数据应该插入的位置
		{
			srcData[j+1] = srcData[j];
			j = j-1;
		}
		srcData[j+1] = tempT;                                      //【4】直接插入
	}
}
/***********************************************************************************************************************
函数原型:
        template<typename DataType>void BinInsertSort(DataType srcData[],int iLength)
函数说明:
        【排序算法2】---------【折半插入排序】
性能分析:
       【1】【折半插入排序算法】是【直接插入排序算法】的改进算法,两者算法的基本性能一致,但是【折半插入排序算法】相比
	            【直接插入排序算法】来说,相互比较的次数减少
	   【2】在【数据量较小】的排序过程中,【折半插入排序算法】往往表现出比【直接插入排序算法更好的性能】
***********************************************************************************************************************/
template<typename DataType>void BinInsertSort(DataType srcData[],int iLength)
{
	DataType      tempT;                           //【1】定义一个哨兵
	int           j     = 0;

	int           iMid  = 0;
	int           iHigh = 0;
	int           iLow  = 0;
	
	for(int i=1;i<iLength;i++)
	{
		tempT = srcData[i];
		iLow  = 0;
		iHigh = i -1;

		while (iLow<=iHigh)
		{
			iMid = (iLow+iHigh)/2;
			if(tempT<srcData[iMid])
			{
				iHigh = iMid - 1;
			}
			else
			{
				iLow = iMid + 1;
			}
		}//while
		for(j=i-1;j>=iLow;j--)
		{
			srcData[j+1] = srcData[j];
		}
		srcData[iLow] = tempT;
	}
}
/***********************************************************************************************************************
函数原型:
        template<typename DataType>void BinInsertSort(DataType srcData[],int iLength)
函数说明:
        【排序算法3】---------【希尔排序】
性能分析:
       【1】【空间效率】:仅使用了常数个辅助单元,因而空间复杂度为O(1);
	   【2】【时间效率】:由于【希尔排序】的【时间复杂度】依赖于【增量序列的函数】,这涉及数学上尚未解决的难题,所以时间
	                     复杂度分析比较困难。但是,希尔排序最坏情况下的【时间复杂度】为O(n*n).
	   【3】【适用性】:【希尔排序算法】仅适用于当【线性表】为【顺序存储】的情况。
***********************************************************************************************************************/
template<typename DataType>void ShellSort(DataType srcData[],int iLength)
{
	for(int d=(iLength/2);d>=1;d = d/2)                    //【1】设置(起始增量;循环条件;增量变化规则)
	{
		for(int k=0;k<d;k++)                               //【2】增量为k,就有k个子序列
		{
			for(int i=k+d;i<iLength;i++)                   //【3】对每一个子序列进行【直接插入排序】
			{
				DataType tempT = srcData[i];
				int      j    = i -1;
				while (tempT<srcData[j])
				{
					srcData[j+1] = srcData[j];
					j = j-1;
				}
				srcData[j+1]=tempT;
			}//for i
		}//for k
	}//for d
}
//===============================================================================================================================
//=====================================================【交换排序算法】==========================================================
//===============================================================================================================================
/***********************************************************************************************************************
函数原型:
        template<typename DataType>void BubbleSort(DataType srcData[],int iLength)
函数说明:
        【排序算法4】---------【冒泡排序】
性能分析:
       【1】【空间效率】:仅使用了常数个辅助单元,因而空间复杂度为O(1);
	   【2】【时间效率】:当初始序列有序时,显然第一趟冒泡后bChangedFlag依然为false(本趟冒泡没有元素交换)。从而直接跳出
	                     循环,比较次数为n-1次,移动次数为0,从而最好情况下的时间复杂度为O(n);当初始序列为逆序时,需要
						 进行n-1趟排序,第i趟排序需要进行n-i次关键字的比较,而且每次比较都需要进行关键字的交换,从而最
						 坏情况下,冒泡排序的时间复杂度为O(n*n);其平均的时间复杂度也为O(n*n).
	   【3】【注意】:【冒泡排序】中所产生的【有序子序列】一定是【全局有序】的(不同于直接插入排序),也就是说有序子序列中
	                     的所有元素的关键字一定小于或者大于【无序子序列】中所有元素的关键字,这样每一趟排序都会将一个元
						 素放在其最终的位置上。
***********************************************************************************************************************/
template<typename DataType>void BubbleSort(DataType srcData[],int iLength)
{
	bool    bChangedFlag = true;                             //【0】表示本趟冒泡排序是否发生了交换
	for(int i=0;i<iLength-1;i++)                             //【1】外循环控制排序的【循环次数】,N个数,需要N-1趟的比较
	{
		bChangedFlag = false;                                //【2】表示本趟冒泡是否发生了交换的标志
		for(int j=0;j<iLength-1-i;j++)
		{
			if(srcData[j]>srcData[j+1])                      //【3】这样经过一次排序后,最后一个数为确定的、最大的数
			{
				int iTemp   = srcData[j];
				srcData[j]  = srcData[j+1];
				srcData[j+1]= iTemp;
				bChangedFlag= true;
			}
		}
		if(bChangedFlag==false)                             //【4】如果本趟遍历后没有发生交换,说明表已经有序
		{      
			break;
			return;
		}
	}//for i
}
/***********************************************************************************************************************
函数原型:
        【1】template<typename DataType>int QuickPartition(DataType srcData[],int iLeft,int iRight)
		【2】template<typename DataType>static void QuickSort(DataType srcData[],int iLeft,int iRight)
函数说明:
        【排序算法5】---------【快速排序】
性能分析:
        【1】【空间效率】:由于【快速排序】是【递归】的,需要借助一个【递归工作栈】来保存每一层【递归调用】的【必要信息】,
		                  其容量应与递归调用的最大深度一致。最好情况下为log2(n+1);最坏情况下,因为要进行n-1次递归调用,
						  所以栈的深度为0(n),平均情况下为O(log2n)
		【2】【时间效率】:【快速排序】的【运行时间】与【划分】是否对称有关,而后者又与具体使用的【划分算法】有关,快速排
		                  序最坏情况发生在两个划分区域分别包含n-1个元素和0个元素时,这种最大深度的不对称,若发生在每一层
						  递归上,即对应于初始排序表基本有序或者基本逆序时,就得到最坏情况下的时间复杂度为O(n*n).
		【3】【时间效率】:好在,【快速排序】的【平均情况】下的【运行时间】与其【最佳情况下】的【运行时间】很接近,而不是
		                  接近【最坏情况下的运行时间】。【快速排序】是【所有内部排序算法】【平均性能最优】的【排序算法】
		【4】在【快速排序】中,并产【有序的子序列】,但每一趟排序后,将一个【基准元素】放到其最终的位置上。
***********************************************************************************************************************/
//【1】对【待排序列】进行一次【快速排序】
template<typename DataType>int QuickPartition(DataType srcData[],int iLeft,int iRight)
{
	DataType   pivotT = srcData[iLeft];
	while (iLeft<iRight)
	{
		while((iLeft<iRight)&&(pivotT<srcData[iRight]))
		{
			iRight--;
		}
		if(iLeft<iRight)
		{
			srcData[iLeft] = srcData[iRight];
			iLeft++;
		}
		while ((iLeft<iRight)&&(srcData[iLeft]<pivotT))
		{
			iLeft++;
		}
		if(iLeft<iRight)
		{
			srcData[iRight] = srcData[iLeft];
			iRight--;
		}
	}//while
	srcData[iLeft] = pivotT;
	return iLeft;
}
//【2】快速排序
template<typename DataType>static void QuickSort(DataType srcData[],int iLeft,int iRight)
{
	if(iLeft<iRight)
	{
		int iPos = QuickPartition(srcData,iLeft,iRight);//【1】选择一个基准,进行一次排序
		QuickSort(srcData,iLeft,iPos-1);                //【2】对左边的子序列进行【快速排序】
		QuickSort(srcData,iPos+1,iRight);               //【3】对右边的子序列进行【快速排序】
	}
}
//===============================================================================================================================
//=========================================================【选择排序】==========================================================
//===============================================================================================================================
/***********************************************************************************************************************
函数原型:
        template<typename DataType>static void SelectSort(DataType srcData[],int iLength)
函数说明:
        【排序算法6】---------【简单选择排序】
性能分析:
        【1】【空间效率】:仅用常数个辅助单元,故空间效率为O(1)
		【2】【时间效率】:从下面的代码中不难看出,简单选择排序过程中,元素移动的操作次数很少,不会超过3(n-1),最好的情况
		                  是移动0次,此时对应的表已经有序;但元素间比较的次数与序列的初始状态无关,始终是n(n-1)/2次,所以
						  时间复杂度为O(n*n)
		【3】【简单选择排序】:是一个不稳定的排序方法。
***********************************************************************************************************************/
template<typename DataType>static void SelectSort(DataType srcData[],int iLenght)
{
	int      iMin = 0;                 //【1】将选出来的最小元素的下标存放在iMin中
	DataType tempT;
	for(int i=0;i<iLenght;i++)
	{
		iMin = i;
		for(int j=0;j<iLenght;j++)
		{
			if(srcData[j]<srcData[iMin])
			{
				iMin = j;
			}
		}//for j
		if(iMin!=i)
		{
			tempT         = srcData[i];
			srcData[i]    = srcData[iMin];
			srcData[iMin] = tempT;
		}
	}//for i
}
/***********************************************************************************************************************
函数原型:
        template<typename DataType>static void SelectSort(DataType srcData[],int iLength)
函数说明:
        【排序算法7】---------【堆排序】
性能分析:
        
***********************************************************************************************************************/
/***********************************************************************************************************************
模块说明:
        【重建堆】----------【调整堆】
***********************************************************************************************************************/
template<typename DataType>static void HeapAdjust(DataType srcData[],int iRootNum,int iLength)
{
	int iLeftChild = 2*iRootNum;                        //【1】iRootNum的左孩子结点序号
	int iRighChild = 2*iRootNum+1;                      //【2】iRootNum的右孩子结点序号

	int iMax       = iRootNum;                          //【3】临时变量,存储大根堆的根节点序号(相对来说)
	if(iRootNum<=iLength/2)                             //【4】如果iRootNum是叶子结点,就不用调整
	{
		if((iLeftChild<=iLength)&&(srcData[iLeftChild]>srcData[iMax]))
		{
			iMax = iLeftChild;
		}
		if((iRighChild<=iLength)&&(srcData[iRighChild]>srcData[iMax]))
		{
			iMax = iRighChild;
		}
		if(iMax!=iRootNum)
		{
			std::swap(srcData[iRootNum],srcData[iMax]);
			HeapAdjust<DataType>(srcData,iMax,iLength); //【5】避免调整之后以iMax为根节点的堆不是大根堆
		}
	}
}
/***********************************************************************************************************************
模块说明:
        【建初堆】
***********************************************************************************************************************/
template<typename DataType>void BuildHeap(DataType srcData[],int iLength)
{
	for(int iNonLeafNodeNum = iLength/2;iNonLeafNodeNum>=1;iNonLeafNodeNum--)
	{
		HeapAdjust<DataType>(srcData,iNonLeafNodeNum,iLength);
	}
}
/***********************************************************************************************************************
模块说明:
        【堆排序】
***********************************************************************************************************************/
template<typename DataType>void HeapSort(DataType srcData[],int iLength)
{
	BuildHeap<DataType>(srcData,iLength);
	for(int i=iLength;i>=1;i--)
	{
		std::swap(srcData[1],srcData[i]);
		HeapAdjust<DataType>(srcData,1,i-1);
	}
}
/****************************************************【Main函数】******************************************************
模块说明:
        控制台应用程序的入口点
***********************************************************************************************************************/
int main(int argc,char* argv[])
{
	std::cout<<"===========================================【排序之前】============================================"<<std::endl;
	for(int i=0;i<ARRAY_LENTGTH;i++)
	{
		std::cout<<srcData[i]<<"  ";
	}
	std::cout<<std::endl;
	//BubbleSort<ElemType>(srcData,ARRAY_LENTGTH);
	//QuickSort(srcData,0,ARRAY_LENTGTH-1);
	//SelectSort<ElemType>(srcData,ARRAY_LENTGTH);
	HeapSort<ElemType>(srcData,ARRAY_LENTGTH-1);
	std::cout<<"===========================================【排序之后】============================================"<<std::endl;
	for(int i=0;i<ARRAY_LENTGTH;i++)
	{
		std::cout<<srcData[i]<<"  ";
	}
	std::cout<<std::endl;
	std::system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值