排序算法大总结

数据结构绝对是重中之重!

排序算法主要有9种


冒泡排序  交换排序  选择排序  插入排序  快速排序  归并排序  堆排序  基排序  希尔排序


每个排序都有自己的适宜场合,有不同的时间复杂度和空间复杂度。

时间复杂度主要是从比较次数移动次数来看

接下来一一介绍



一、冒泡排序

算法思想:

1.待排序的数据看作气泡,通过n-1趟比较,从而使较小的数据浮在上层(前),较大的数据沉在下层(后)。

2.每一趟比较,从下层开始比较两个相邻的数据,如果上层的数据大于下层的数据,则交换;否则,不移动顺序继续往上比较。

void BubbleSort(int n,List &A){
	for (int i=0;i=i+1;j--)
		{
			if (A[j].key

  比较次数 移动次数    
最好情况 n-1 0    
最坏情况 n(n-1)/2 3n(n-1)/2    



平均时间复杂度 最差时间复杂度 空间复杂度
O(N*N) O(N*N) O(1)


二、快速排序

算法思想:(分治算法)

1.找基准元素。FindPivot(int i,int j)

   作用是,使数列左侧都比基准元素要小,右侧都比基准元素要大。

   如何选取?从左侧开始查找两个不同数值中的较大值作为基准元素。如果找不到两个不相同的元素,则返回0。排序结束。例如 3 3 2 5 6 1 0 则选取3为基准元素。

2.划分

   (1)l表示从左边开始遍历的下标,r表示从右边开始遍历的下标。l为指向数列中从左边开始第一个比基准元素大的值;r为数列中从右边开始第一个比基准元素小的值。

   (2)如果l

      如果l>r,也即l=r+1。数列从l-1和l之间划分成两部分。

3.对于划分后的两个数列,继续采用快排算法。

int FindPivot(int i,int j,List &A){//选取基准元素,从数列左侧查找两个不同数值中的较大值为基准元素
	for (int k=i+1;k<=j;k++)
	{
		if (A[k].key>A[i].key)
			return k;
		else if (A[k].key=Pivot;r--);
		if (l
不过这里有点小错误啦~

就是结束递归结束条件设置的是pivotindex!=0,但是确实第一次可能pivotindex=0,如果结束,就没有进行排序。
排序算法大总结_第1张图片



最好情况

每次划分后,划分点左侧和右侧长度相同,则时间复杂度为O(nlogn)

T(n)<=2T(n/2)+n
      <=2(2T(n/4)+n/2)+n

      <=4(2T(n/8)+n/4)+2n

      ......

     <=nT(1)+nlogn=O(nlogn)

最坏情况

每次划分只得到一个比上一次划分少一个记录的子序列

平均时间复杂度 最差时间复杂度 空间复杂度
O(nlogn) O(N*N) O(logn)


三、选择排序(selectsort)

算法思想:

与冒泡排序类似,冒泡排序,是找到比前面元素小的元素就交换。而选择排序则是在序列中找到最小的元素,然后与最前面的元素互换

void SelectSort(int n,List &A)
{
	int low;
	int lowindex;
	for (int i=0;i
性能分析
1.移动次数
  最好情况 0次
  最坏情况 3(n-1)
2.比较次数
   (n-1)+(n-2)+(n-3)+...+2+1=n(n-1)/2=O(n2)
3.时间复杂度 O(n2)
4.空间复杂度 O(1) (需要一个low 和lowindex的空间)


四、堆排序

算法思想:

数列->完全二叉树->初始建堆->整理堆

1.将数列按照完全二叉树的下标排成完全二叉树。

2.对该完全二叉树用PushDown算法依次对n/2,n/2-1,n/2-2,...1建堆。

3.取堆顶元素一定是最小值。

4.交换第N个和第1个元素。重复3。知道取遍所有元素。


首先必须明确一点,二叉树有完整的排序规则,从1开始到n.若某个节点为i,左孩子为2*i,右孩子为2*i+1.


void PushDown(int first,int last,List &A){//整理堆算法
	int r=first;
	while (r<=last/2)//如果r<=last/2,则为堆。否则都为叶子节点
	{
		if (r==last/2 && last%2==0)//r节点只有左节点
		{
			if (A[r].key>A[last].key)
			{
				swap(A[r],A[last]);
			    
			}
			r=last;
			
		}
		else if (A[r].key>A[2*r].key && A[2*r].key<=A[2*r+1].key)//r节点有左右孩子,左孩子小于根节点,左孩子小于右节点,则将根节点和左孩子交换
		{
			swap(A[r],A[2*r]);
			r*=2;
		}
		else if (A[2*r+1].key=1;i--)
		PushDown(i,n,A);
	for (i=n;i>=2;i--){ 
		swap(A[1],A[i]);
		PushDown(1,i-1,A);	
	}
}
对于堆排序一样,输入和输出都从1开始而非0开始,
for (int i=1;i<=n;i++)
{
printf("%d\n",A[i].key);
}
堆排序的时间复杂度为O(nlogn)
五、直接插入排序

算法思想:

每次将待排序的数值插入有序区的相应位置。

什么才是相应位置,在该位置前面找不到元素比他大,则这个位置就是正确的相应位置。

排序算法大总结_第2张图片

对于直接插入排序,和堆排序一样,输入和输出都从1开始而非0开始,具体原因是因为第一位被哨兵占用。
for (int i=1;i<=n;i++)
{
printf("%d\n",A[i].key);
}

void InsertSort(int size,List &A)
{
	A[0].key=-100000;//哨兵
	for (int i=1;i<=size;i++)
	{
		int j;
		j=i;//A[j]为待排序的值,在数列中i之前的为有序区

		//将j插入正确的位置
		while (A[j-1].key>A[j].key)//如果j之前的数值比A[j]大,则将该数值与A[j]交换
		{
			swap(A[j-1],A[j]);
			j=j-1;
		}
	}
}


性能分析
1. 最好情况(正序)
   比较次数:n-1
   移动次数 2(n-1)
   时间复杂度 O(n)
2. 最坏情况 (反序)
   比较次数  2+3+4+...+n
   移动次数  3+4+5+...+n+1
   时间复杂度 O(n2)
3.平均情况
   比较次数 (2+3+4+...+n)/2
   移动次数 (3+4+5+...+n+1)/2
   时间复杂度 O(n2)
4.空间复杂度 O(1),适用于基本有序,或者,尺寸很小的数列

六、归并排序

算法思想:

1.第一遍归并是一一归并。第二遍是二二归并,第三遍是四四归并。第n遍就是2的N次方。

2.归并是两个列表的归并,指针i,j分别指向A中的两段{p...q-1}{q...r}.比较A[i],A[j]的大小,将小者赋给B[k]。如果两段序列不一样长,则将剩下的直接赋给B[].【Merge(int p,int q,int r,List A,List B)】

3.每一遍归并,设当前归并长度为h,,从序列开头开始,尽量归并2个h的序列,直达归并不了,剩下的序列,如果大于h小于2h,则归并两段,如果小于h,则直接复制到归并后的序列。【MergePass(int n,int h,List A,List B)】

4.归并排序【MergeSort(int n,List &A)】,当前归并序列长度大于n,终止排序。若小于n,开始一趟排序。

/****************归并排序*********************/
void Merge(int p,int q,int r,List A,List B)//A中两个序列【p...q-1】【q...r】合并到B
{
	int i,j,k;
	i=p;
	j=q+1;
	k=p;
	while (i<=q&&j<=r)
	{
		if (A[i].key <= A[j].key)
			B[k++]=A[i++];
		else
			B[k++]=A[j++];
	}
	while (i<=q)
	{
		B[k++]=A[i++];
	}
	while (j<=r)
	{
		B[k++]=A[j++];

	}

}

void MergePass(int n,int h,List A,List B){//把A中长度为h的相邻序列归并为长度为2h
	int i;
	for (i=1;i+2*h-1<=n;i+=2*h)//归并长度为h的两个子序列
	{
		Merge(i,i+h-1,i+2*h-1,A,B);
	}
	if (i+h-1

性能分析
1.时间复杂度 O(nlogn)
  一趟归并将A[1...N]将两个长度为h的相邻子序列归并到B[1...N]中,时间复杂度为O(n)。
  共需要logn趟排序。 则归并排序时间复杂度为 O(n)*logn=O(nlogn)
2.空间复杂度 O(n)
  需要长度与A相同的的序列B。则 空间复杂度 O(n)

另一种是折半归并,这样就不用了一趟一趟递增归并长度h的归并,
1.两序列分解,A[LOW...HIGH]一分为二,mid=LOW+HIGH
2.递归的对序列A[LOW]...A[MID]和A[MID+1]...A[HIGH]进行归并排序
3.将两个已排序子序列归并为一个有序序列。

void MergeSort_q(int low,int high,List &A,List &B){//折半归并
	int mid=(low+high)/2;//两序列分解,A[LOW...HIGH]一分为二,mid=LOW+HIGH
	if (low
void main()
{
	List B;
	input_1();
	MergeSort_q(1,size,L,B);
	output_1(size,B);
}


根据比较关键字的大小排序,复杂度下限为O(nlogn)

而基数排序不是根据比较关键字的大小,而是根据构成关键字的每个分量的值,比较大小,从而排列记录。
关键字的各分量的取值范围必须为有限

七、基数排序

算法思想:

1.



(未完待续)







你可能感兴趣的:(学习成长记,排序算法)