排序(二):归并排序,快速排序,堆排序

归并排序

归并排序是基于“归并”思想的排序方法,利用two pointers思想。这里说的是二路归并,就是将两个小集合归并为一个大集合,依次类推,当然要将集合合并,但给我们的只有一个集合,所以要先划分成n/2个个数为2的集合,动图演示在最下方。
方法一:递归实现,这个方法其实很简单,就反复将当前的区间[left,right]分为两半,对两个子区间[left,mid],[mid+1,right]分别递归进行归并排序,然后合并两个序列即可。

代码(递归实现)

#include<stdio.h>
const int MAXN=1e5;
void merge(int A[],int L1,int R1,int L2,int R2)
{
	int i=L1,j=L2,index=0;
	int temp[MAXN];//利用一个临时数组 
	while(i<=R1 && j<=R2)//利用双指针应用 
	{
		if(A[i]<=A[j])//如果左边的元素比右边的小则先进入 
			temp[index++]=A[i++];
		else//否则,右边先进入 
			temp[index++]=A[j++];
	}
	//把没进入的一方全部进入。 
	while(i<=R1)	temp[index++]=A[i++]; 
	while(j<=R2)	temp[index++]=A[j++];
	for(i=0;i<index;i++)	A[L1+i]=temp[i];
}
void mergeSort(int A[],int left,int right)
{
	if(left<right)
	{
		int mid=(left+right)>>1;//划分为一个元素的集合 
		mergeSort(A,left,mid);//递归左边 
		mergeSort(A,mid+1,right);//递归右边 
		merge(A,left,mid,mid+1,right);//将两个集合合并为一个集合 
	}
}
int main()
{
	int a[8]={18,7,8,6,33,2,9,1};
	int len=8;
	mergeSort(a,0,len-1);
	for(int i=0;i<len;i++)	printf("%d ",a[i]);
	return 0;
}

性能分析:空间复杂度为O(n),时间复杂度为O(nlogn),稳定的排序。

快速排序

快速排序也利用了two pointers(双指针)的思想。
1.将第一个元素作为基准,找到一个位置,使得第一个元素的左边不超过基准,右边不小于基准。
2.对元素的左侧和右侧分别递归重复进行1操作,直到当前调整区间的长度不超过1。
注意,当序列是有序的时候,使用快排的时间复杂度将降为O(n²)。改进的方法有很多,这里只说一下三数取中法。

代码

#include<stdio.h>
#include<algorithm>
using namespace std;
void quickSort(int A[],int left,int right)
{
	int L=left,R=right;
	//三数取中法 
	int mid=(left+right)>>1;
	if(A[mid]>A[right])	swap(A[mid],A[right]);
	if(A[left]>A[right]) swap(A[left],A[right]);
	if(A[left]>A[mid])	swap(A[left],A[mid]);
	if(right-left<3)	return;
	int temp=A[mid];//选取中间的元素为基准 
	A[mid]=A[left];//将第一个元素赋值给中间的元素 
	while(L<R)//找出左边比temp小的右边比temp大的位置 
	{
		while(L<R && temp<A[R])	R--;
			A[L]=A[R];
		while(L<R && temp>=A[L]) L++;
			A[R]=A[L];
	}
	A[L]=temp;
	if(left<L-1)	quickSort(A,left,L-1);//递归左边 
	if(L+1<right)	quickSort(A,L+1,right);//递归右边 
}
int main()
{
	int a[8]={18,7,8,6,33,2,9,1};
	int len=8;
	quickSort(a,0,len-1);
	for(int i=0;i<len;i++)	printf("%d ",a[i]);
	return 0;
} 

性能分析:空间复杂度为O(logn),时间复杂度为O(nlogn),不稳定的排序。

堆排序

首先来浅谈一下堆的概念,堆是一个完全二叉树,其有大根堆和小根堆之分。大根堆就是根节点比它的左子树和右子树都大,小根堆就是根节点比它的左子树和右子树都小。这里我采用的是大根堆,方法是向下调整的方法,所谓向下调整就是让调整的某点依次向下比较,选取左子树和右子树中最大的那个,与其交换后在向下调整,直至没有左子树或右子树或者子树没有比它更大的数。怎样建堆是关键,我们不需要遍历整个数,只需遍历到n/2到0即可,因为n/2要么是最后一层,要么是倒数第二层。
最后就是排序了,排序的方法就是依次将树的根节点和最后一个节点交换后,然后对[0,n-1]向下取整。动画演示

代码

#include<stdio.h>
#include<algorithm>
using namespace std;
int a[8]={18,7,8,6,33,2,9,1};
void downAdjust(int low,int high)
{
	int i=low,j=i*2+1;//i为子树的根节点,j为左节点(从0开始)
	while(j<=high)
	{
		if(j+1<=high && a[j+1]>a[j])	j++;//如果右孩子比左孩子大,那么j存储右孩子
		if(a[j]>a[i])
		{
			swap(a[j],a[i]);//交换 
			i=j;//将j作为子树的根节点
			j=i*2+1;//j为i的左节点 
		}
		else break;
	} 
}
void createHeap(int n)//创建堆 
{
	for(int i=n/2;i>=0;i--)
		downAdjust(i,n);
}
void Heapsort(int n) 
{
	createHeap(n);
	for(int i=n;i>0;i--)//从下往上,
	{
		swap(a[i],a[0]);// 先将跟节点和最后一个节点交换,
		downAdjust(0,i-1);//然后堆0到倒数第二的节点做向下调整。  
	}
}
int main()
{
	int len=8;
	Heapsort(len-1);
	for(int i=0;i<len;i++)	printf("%d ",a[i]);
	return 0;
} 

性能分析:空间复杂度为O(1),时间复杂度为O(nlogn),不稳定的排序。
归并排序
在这里插入图片描述
快速排序
在这里插入图片描述
堆排序
在这里插入图片描述

发布了67 篇原创文章 · 获赞 1 · 访问量 1320

猜你喜欢

转载自blog.csdn.net/qq_45249273/article/details/104727625