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


    在这里插入图片描述

    一、直接插入排序(下标从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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    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. 由长度不等的归并段,进行多路平衡归并,需要构造最佳归并树
  • 相关阅读:
    嵌入式系统工程师错题总结
    Python基础全套资料:从入门到入门
    Java项目:汽车商城管理系统(java+SpringBoot+VUE+Maven+Mysql)
    vue2使用国际化vue-i18n详解+ES6的import和export区别
    php字符串的截取方式
    鱼眼图像去畸变python / c++
    写个俄罗斯方块,感受嵌入式linux应用开发
    Linux:Centos7.x系统,无效的密码问题处理
    Asp .Net Core 系列:集成 Ocelot+Nacos+Swagger+Cors实现网关、服务注册、服务发现
    【Java篇】回顾二进制文件和字节流
  • 原文地址:https://blog.csdn.net/m0_46638350/article/details/127698957