数据结构-考研-第八章——排序(各种算法总结 + 动态演示)

在这里插入图片描述

一、直接插入排序(下标从1开始)

  1. 算法思想:每次将一个待排序的记录按其关键字大小,插入到前面已经排好的子序列中,直到全部记录插入完成。
  2. 代码实现
//不带“哨兵”
void InsertSort(int A[],int n){
	int i,j,temp;
	for(i=2;i<n;i++){
		if(A[i]<A[i-1]){		//发现倒序
			temp=A[i];
			for(j=i-1;j>=0&&A[j]>temp;--j)		//寻找合适的插入位置
				A[j+1]=A[j];
			A[j+1]=temp;
	}
}

//带“哨兵”
void InsertSort(int A[],int n){
	int i,j;
	for(i=2;i<=n;i++){
		if(A[i]<A[i-1]){		//发现倒序
			temp=A[i];
			for(j=i-1;A[j]>A[0];--j)		//寻找合适的插入位置
				A[j+1]=A[j];
			A[j+1]=A[0];
	}
}
  1. 具体实例(49,38,65,97,76,13,27,49)
    在这里插入图片描述

  2. 算法分析

  1. 空间复杂度:,使用了常数个辅助单元,因而空间复杂度为O(1)
  2. 时间复杂度:最差O(n^2)、最好O(n)
  3. 稳定性:稳定
  4. 链表实现:时间复杂度依然是O(n^2)

二、折半插入排序(下标从1开始)

  1. 算法思想:对直接插入排序算法的改进,在对有序部分进行折半查找并插入
  2. 代码实现
void InsertSort(int A[] int n){
	int i,j,low,high,mid;
	for(i=2;i<=n;i++){
		A[0]=A[i];
		low=1;high=i-1;
		while(low<=high){
			mid=(low+high)/2;
			if(A[mid]>A[0]) high=mid-1;			//可用A[0],也可用A[i]
			else low=mid+1
		}
		for(j=i-1;j>=high+1;--j)	//注意这里只能用high,不能用low,mid
			A[j+1]=A[j];
		A[high+1]=A[0];
	}
}
  1. 算法分析
  1. 与直接插入排序相比,比较关键字次数减少了,但是移动元素的次数没有改变,所以时间复杂度仍然是O(n^2);
  2. 只能用于顺序存储的线性表;

三、希尔排序(下标从1开始)

  1. 算法思想:先追求表中元素的部分有序,再逐渐逼近全局有序。
  2. 代码实现(可以对比直接插入排序)
void ShellSort(int A[],int n){
	int i,j;
	for(dk=n/2;dk>=1;dk/2)
		for(i=dk+1;i<=n;++i){
			if(A[i]<A[i-dk]){		
				A[0]=A[i];
				for(j=i-dk;A[j]>A[0]&&j>0;j-=dk)		
					A[j+dk]=A[j];
				A[j+dk]=A[0];
		}
}
  1. 具体实例
    在这里插入图片描述

  2. 算法分析

  1. 空间复杂度:仅使用常数个辅助单元,因而空间复杂度为O(1);
  2. 时间复杂度:最坏情况下O(n^2);
  3. 稳定性:因为相同元素可能放在不同子序列中,不同子序列内的元素交换,
  4. 无法确定相同元素不调换排序位置,所以希尔排序是一种不稳定的排序。

四、冒泡排序(下标从0开始)

  1. 算法思想:每趟冒泡排序把序列中最小元素放到序列的最终位置
  2. 代码实现
void BubbleSort(int A[],int n){
	for(int i=0;i<n;i++){
		bool flag=false;
		for(int j=n-1;j>i;j--)		//j>I表明只在未排序序列中交换
			if(A[j-1]>A[j]){
				swap(A[j-1],A[j]);
				flag=true;
			}
		if(!flag) return;	//如果一趟下来都没有发生交换,那么表明整个序列已经有序
	}
}	
  1. 具体实例
    在这里插入图片描述

  2. 算法分析

  1. 空间复杂度:仅用了常数个辅助变量,因而空间复杂度为O(1);
  2. 时间复杂度:最好为O(n),最差为O(n^2);
  3. 适用范围:顺序表、链式表

五、快速排序(下标从0开始)

  1. 算法思想:在待排序表L[1…n]中任取一个元素pivot作为基准,通过一趟排序将待排序划分为独立的两个部分,使得小于pivot的所有元素放在左边,大于pivot的所有元素放在右边;
  2. 代码实现
int Partion(int A[],int low,int high){
	int pivot=A[low]
	while(low<high){
		while(low<high&&A[high>=pivot]) --high;
		A[low]=A[high];
		
		while(low<high&&A[low]<=pivot) ++low;
		A[high]=A[low]; 
	}
	A[low]=pivot;
	return low;
}

void QuickSort(int A[],int low,int high){
	if(low<high){
		int position=Partion(A,low,high);
		QuickSort(A,low,position-1);
		QuickSort(A,position+1,high);
	}
}
  1. 具体实例
    在这里插入图片描述
  2. 算法分析
  1. 空间复杂度:因为在递归过程中,每个递归都需要一个辅助变量,那么递归的次数就等于辅助变量的次数,因为递归的次数最好为log2 n,所以空间复杂度最好为O(n),最差为O(n)。
  2. 时间复杂度:最好O(nlog2n),最坏O(n^2)
  3. 稳定性:不稳定

六、 简单选择排序(下标从0开始)

  1. 算法思想:第 i 趟排序从L[i…n]中选择关键字最小的元素与L[i]交换。
  2. 代码实现
void SelectSort(int A[],int n){
	for(int i=0;i<n-1;i++){
		int min=i;
		for(int j=i+1;j<n;j++)
			if(A[j]<A[min]) min=j;
		if(min!=i)
			swap(A[i],A[min]);
	}
}
  1. 具体实例
    在这里插入图片描述
  2. 算法分析
  1. 空间复杂度:使用一个交换变量,所以空间复杂度为O(1)
  2. 时间复杂度:O(n^2)
  3. 稳定性:不稳定
  4. 适用范围:顺序表、链表

七、堆排序(下标从1开始)

  1. 算法思想
  1. 堆:可以理解为顺序存储的完全二叉树
  2. 大根堆:完全二叉树,根>=左右
  3. 小根堆:完全二叉树,根<=左右
  4. 堆排序分为两个过程:堆初始化、输出堆顶后调整新堆
  1. 代码实现(自下而上)
void HeadAdjust(int A[],int k,int len){		//调整指定根节点A[k]
	A[0]=A[k];
	for(int i=k*2;i<=len;i*=2){
		if(i<len&&A[i]<A[i+1]) i++;		//不管要不要交换,先选出最大的子结点
		if(A[0]>=A[i]) break;		//不用交换,而且后面是一定调整好的了
		else{
			A[k]=A[i]
			k=i;					//这里与A[K]=A[0]相呼应,其实也可以选择A[i]=A[0],每次都交换
		}
	}
	A[k]=A[0];
}

void BuildMaxHeap(int A[],int len){	//从最后一个子树根节点开始调整,调整全部根节点
	for(int i=len/2;i>0;--i)			
		HeadAdjust(A,i,len);
}

void HeapSort(int A[],int len){
	BuildMaxHeap(A,len);			//堆初始化
	for(i=len;i>1;i--){				
		Swap(A[i],A[1]);			//将最大值放在最后,然后重新在指定位置调整
		HeapAdjuest(A,1,i-1)		//截断最后一位,并且重新从第一位调整
	}
}
  1. 具体实例

  2. 算法分析

  1. 空间复杂度:使用了常数个辅助单元,所以为O(1)
  2. 时间复杂度:建堆O(n)+堆排序O(nlogn)=O(nlogn)
  3. 稳定性:不稳定

八、归并排序

  1. 算法思想:把两个或多个已经有序的序列合并成一个
  2. 代码实现
//创建辅助数组B
int *B=(int*)malloc(n*sizeof(int));

//A[low,...mid],A[mid+1,...,high]各自有序,将这两部分归并
void Merge(int A[],int low,int mid,int high){
	int i,j,k;
	for(k=low;k<=high;k++) B[k]=A[k];
	for(i=low,j=mid+1,k=low;i<=mid&&j<=high;k++){
		if(B[i]<=B[j]) A[k]=B[i++];
		else A[k]=B[j++];
	}
	//剩下没有规定完的部分赋值到尾部,while只会执行一个
	while(i<=mid) A[k++]=B[i++];
	while(j<=high) A[k++]=B[j++]
}

void MergeSort(int A[],int low,int high){
	if(low<high){						//只有low、high指向了同一位才会停止递归
		int mid=(low+high)/2;
		MergeSort(A,low,mid);			//low、mid、high都是传下来的
		MergeSort(A,mid+1,high);
		Merge(A,low,mid,high);
	}
}
  1. 具体实例
    在这里插入图片描述
  2. 算法分析
  1. 空间复杂度:这是本章占用辅助空间最多的排序算法,为O(n)
  2. 时间复杂度:每趟归并为O(n),排序为O(nlogn)
  3. 稳定性:稳定

九、基数排序

  1. 具体实例
    在这里插入图片描述
  2. 算法分析
  1. 使用范围:通常用于链表
  2. 空间复杂度:辅助存储空间为r个队列,即为O( r);
  3. 时间复杂度:d趟收集分配,一趟分配需要O(n),一趟收集需要O( r),所以时间复杂度为O(d(n+r))
  4. 稳定性:稳定

十、外部排序

  1. 考察内容
  1. 外部排序指待排序文件较大,内存一次放不下,需存放在外存的文件的排序
  2. 外部排序常采用归并排序
  3. 为减少平衡归并中外存读写次数所采取的方法:增大归并路数和减少归并段个数
  4. 利用败者树增大归并路数
  5. 利用置换-选择排序增大归并段长度来减少归并个数
  6. 由长度不等的归并段,进行多路平衡归并,需要构造最佳归并树
  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值