数据结构与算法分析学习笔记--第五章--排序

本文就将列出希尔排序、插入排序、堆排序、归并排序及快速排序的c++\c 实现


插入排序

//插入排序
//时间复杂度o(n2)
template <typename T>
void InsertSort(T arrayType[],int n)
{
	int j,p;
	T temp;
	for(p=1; p<n; ++p)
	{
		temp = arrayType[p];
		for(j=p;j>0 && arrayType[j-1]>temp;--j)
		{
			arrayType[j] = arrayType[j-1];
		}
		arrayType[j] = temp;
	}
}

希尔排序

//希尔排序
//使用希尔增量时,时间复杂度最坏o(n2),希尔增量为h1 = [N/2],hk = [hk/2].
template <typename T>
void ShellSort(T arrayType[],int n)
{
	int j,i,increment;
	T temp;
	for(increment = n / 2; increment>0; increment /= 2)
	{
		for(i = increment;i<n;i++)
		{
			temp = arrayType[i];
			for(j=i; j>=increment; j -= increment)
			{
				if (temp < arrayType[j - increment])
				{
					arrayType[j] = arrayType[j - increment];
					//Print(arrayType,n);
				}
				else
					break;
			}
			arrayType[j] = temp;
		}
	}
}

堆排序

#include <stdio.h>
#define LeftChild(i) 2*(i)
void PercDown(int A[], int i,int N)
{
	int child;
	int Temp;
	for(Temp = A[i];LeftChild(i) < N;i=child)
	{
		child = LeftChild(i);
		if(child != N -1 && A[child + 1] > A[child])
		{
			child++;
		}
		if(Temp < A[child])
		{
			A[i] = A[child];
		}
		else
			break;
	}
	A[i] = Temp;
}
void Swap(int *a,int *b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}
void HeapSort(int A[],int N)
{
	int i;
	for(i = N/2; i>=0; i--)		//build heap
	{
		PercDown(A,i,N);
	}
	for(i = N-1; i>0; i--)
	{
		//printf("%d ",A[0]);
		Swap(&A[0],&A[i]);		//delete max
		PercDown(A,0,i);        //rebuilt heap
	}
}

归并排序

#include <memory>
#include <stdio.h>
/*
归并排序:
    归并排序以O(NlogN)最坏情形运行时间运行,而所使用的比较次数几乎是最优的。它是递归算法一个很好的实例。
缺点:
    虽然归并排序的时间复杂度为O(NlogN),但是它很难用于主存排序,主要问题在于合并两个排序的表需要线性附加内存,
在整个算法中还要花费将数据拷贝到临时数组再拷贝回来这样一些附加的工作,其严重放慢了排序的速度。这种拷贝可以通过
在递归交替层次时转换A和TempArray的较色得到避免。

*/

//合并函数
//lpos = start of left half
//rpos = start of right half
//rightEnd = end of right half
//rpos - 1 = end of left half
void Merge(int A[],int TempA[], int lpos,int rpos, int RightEnd)
{
	int LeftEnd;  
	LeftEnd = rpos - 1;
	int Tmppos;
	Tmppos = lpos;
	int NumElements;
	NumElements = RightEnd - lpos + 1;

	while(lpos <= LeftEnd && rpos <= RightEnd)
	{
		if(A[lpos] <= A[rpos])
		{
			TempA[Tmppos++] = A[lpos++];
		}
		else
		{
			TempA[Tmppos++] = A[rpos++];
		}
	}

	while(lpos <= LeftEnd)	//当右半部分先走完时
	{
		TempA[Tmppos++] = A[lpos++];
	}
	while (rpos <= RightEnd)//当左半部分先走完时
	{
		TempA[Tmppos++] = A[rpos++];
	}
	for(int i = 0;i<NumElements;i++,RightEnd--)
	{
		A[RightEnd] = TempA[RightEnd];
	}
}
void Msort(int A[], int TempA[], int left, int right)
{
	int center;
	if(left < right)
	{
		center = (left + right) / 2;
		Msort(A,TempA,left,center);
		Msort(A,TempA,center+1,right);
		Merge(A,TempA,left,center+1,right);
	}
}
void MergeSort(int A[],int N)
{
	int *TempArray;
	TempArray = (int *)malloc(N * sizeof(int));
	if(TempArray != NULL)
	{
		Msort(A,TempArray,0,N-1);
		free(TempArray);
	}
	else
	{
		printf("out of memory");
	}
}

快速排序

/*
QuickSort(快速排序):
    是在实践中最快的已知排序算法,它的平均运行时间是ONlog(N)。该算法之所以特别快,主要是由于非常精炼的和高度优化的内部循环。
它的最坏情形的性能为O(N^2),但稍加注意就可以避免这种情况。
    快速排序是一种分治的递归算法,选取枢纽元(数组中的任意元素v,最好为中间元素),将小于v的放在v的左边,大于v的放在v的右边。
然后再将着两部分分别进行快速排序。

枢纽元的选取直接会影响排序效率:
	一种错误的方法:将第一个元素作为枢纽元。如果输入时随机的,这种选取方法是可以接受的。如果输入时预先排好序或者反序的,
  那么这样的枢纽元就是很劣质的选择,因为所有的元素不是被划入S1,就是被划入S2.
	一种安全的做法:随机选取枢纽元。如果随机数生成器没问题的话,一般这种发法是可以接受的。但是额外产生了随机数生成的代价,并且该代价是很高的。
	
常用的选择枢纽元的方法:
	三数中值分割法(Median-of-Three Partitioning)
	一组N个数的种植是第(N/2)个最大的树。枢纽元的最好选择是数组的中值,但是这个中值很难计算。所以我们一般取左端、右端、中心位置处的值,然互取着三个数的中值作为枢纽元。

小数组:
    对于很小的数组(N<=20),快速排序不如插入排序好。因为快排是递归的,所以这样的情形还经常发生。通常解决的办法是对于小数组不递归地使用快速排序,而代之以诸如插入
排序这样的对小数组有效的排序算法。使用这种策略实际上可以节省大约15%的运行时间。一种好的截止范围是N=10,虽然在5--20之间任一截止范围都有可能产生类似的结果。这种做法也避免
了一些有害的特殊情形,如取三个元素的中值而实际上却只有一个或两个元素的情况。
*/
#include <stdio.h>
void Swap(int *a,int *b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}

/*
Return Median of left , right and center
Order these and hide the pivot  //将中值与右值交换,隐藏枢纽元
*/
int Median3(int A[], int left,int right)
{
	int center = (left + right)/2;
	if(A[left] > A[center])
		Swap(&A[left],&A[center]);
	if(A[left] > A[right])
		Swap(&A[left],&A[right]);
	if(A[center] > A[right])
		Swap(&A[center],&A[right]);
	//A[left]<=A[center]<=A[right]

	//hide A[center]
	Swap(&A[center],&A[right-1]);   //之所以用right - 1是因为A[right]比A[center]大,就不需要进行比较了。
	return A[right - 1];
}

void InsertSort(int arrayType[],int n)
{
	int j,p;
	int temp;
	for(p=1; p<n; ++p)
	{
		temp = arrayType[p];
		for(j=p;j>0 && arrayType[j-1]>temp;--j)
		{
			arrayType[j] = arrayType[j-1];
		}
		arrayType[j] = temp;
	}
}




#define Cutoff (3) //这里设小数组的标准为3
void Qsort(int A[],int left,int Right)
{
	int i,j;
	int Pivot;
/*1*/	if(left + Cutoff <= Right)
		{
/*2*/		Pivot = Median3(A,left,Right);
/*3*/		i = left;
/*4*/		j = Right - 1;
/*5*/		for(;;)
			{
/*6*/			while(A[++i] < Pivot){}  //左边的哨兵找到不比枢纽元小的值停止
/*7*/			while(A[--j] > Pivot){}  //右边的哨兵找到不必枢纽元大的值停止
/*8*/			if(i<j)
/*9*/				Swap(&A[i],&A[j]);
			    else
/*10*/				break;
			}
/*11*/		Swap(&A[i],&A[Right - 1]);		//主循环结束后将枢纽元还原

/*12*/		Qsort(A,left,i - 1);
/*13*/		Qsort(A,i + 1,Right);   //引文A[i]是枢纽元
/*14*/	}
     	else   //小数组的时候采用插入排序
/*15*/		InsertSort(A+left,Right - left + 1);
}

/*
此处注意:
上面的函数中,3--9行用下面的代码替换时不可以的,如果用下面的代码替换,当遇到与枢纽元相等的元素的时候,会陷入死循环
i = left;
j = Right - 1;
for(;;)
{
	while(A[i] < Pivot){i++;}  //左边的哨兵找到不比枢纽元小的值停止
	while(A[j] > Pivot){j++;}  //右边的哨兵找到不必枢纽元大的值停止
	if(i<j)
		Swap(&A[i],&A[j]);
	else
		break;
}

*/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值