数据结构之8种排序算法

news2024/9/30 5:29:53

文章目录

      • 1.插入排序
      • 2.希尔排序:
      • 3.冒泡排序
      • 4.快速排序
      • 5.简单选择排序
      • 6.堆排序
        • 在堆中插入新元素:
        • 在堆中删除一个元素:
      • 7.归并排序
      • 8.基数排序
      • 9.外部排序

排序算法

1.插入排序

每次将一个待排序的记录按其关键字大小插入到前面已经排好序的子序列中,直到全部记录插入完成。

public void insertSort(int A[],int n){
	for(int i=1;i<n;i++){
		if(A[i]<A[i-1]){ //后一个元素小于前一个元素
			int temp=A[i]; //将后一个元素暂存起来
			for(int j=i-1;j>=0 && A[j]>temp;--j){ //检查前面已经排好序的元素
				A[j+1]=A[j];//把比这个元素大的值都往后移动
				A[j+1]=temp; //复制到插入位置  
			}
		}
	}
}

空间复杂度:O(1)
时间复杂度:
最好情况下:原本就有序,每一趟只需要进行关键字对比,不需要移动元素,共n-1趟处理 O(n)
最坏情况下:原本为逆序,每一趟都要对比和移动,比如第i趟,对比i+1次,移动i+2次 O(n^2)
平均时间复杂度 O(n^2)

稳定的排序算法

优化-----折半插入排序
先用折半查找找到应该插入的位置,再移动元素

 public void insertSort(int A[],int n){
 	for(int i=2;i<=n;i++){ //将A[2]-A[n]插入到前面已排序序列
 		A[0]=A[i];  //将A[i]暂存到A[0]
 		int low=1;
 		int high=i-1;
 		while(low<=high){ //折半查找
 			int mid=(low+high)/2; //取中间点
 			if(A[mid]>A[0]){
 			//查找左半子表
 				high=mid-1;
 			}else{
 			//查找右半子表
 				low=mid+1;
 			}
 			for(int j=i-1;j>=high+1;--j){
 				A[j+1]=A[j];//向后移元素
 				A[high+1]=A[0];//插入 
 			}

 		}
 	}
 }

当low>high时折半查找停止,将[low,i-1]内元素全部右移,并将A[0]复制到low位置
将每个要进行排序的元素先存进A[0]

2.希尔排序:

先将待排序表分割成若干[i,i+d,i+2d…i+kd]的子表,对各个子表分别进行直接插入排序,缩小增量d,重复上面过程,直到d=1为止

 public ShellSort(int A[],int n){
 	 for(int d=n/2;d>=1;d=d/2){ //d是增量
 	 	for(int i=d+1;i<=n;++i){
 	 		if(A[i]<A[i-d]){ //需要将A[i]插入有序增量子表
 	 			A[0]=A[i]; //暂存在A[0]
 	 			for(int j=i-d;j>0 && A[0]<A[j];j-=d){
 	 				A[j+d]=A[j]; /记录后移,查找插入位置
 	 			}
 	 			A[j+d]=A[0];//插入
 	 		}
 	 	}
 	 }
 }

时间复杂度:和增量序列d1,d2,…dn的选择有关,最坏情况下时间复杂度O(n^2),优于直接插入排序

不稳定的排序算法
仅可用于顺序表,不适合链表

3.冒泡排序

交换排序可分为冒泡排序和快速排序
基于“交换”的排序,比较相邻的两个元素,如果是逆序则进行交换

public void sort(int A[],in n){
	for(int i=0;i<n-1;i++){
		for(int j=0;j<n-2;j++){
			if(A[j]>A[j+1]){
				int tmp=A[j];
				A[j]=A[j+1];
				A[j+1]=tmp;
			}
		}
	}
}

空间复杂度O(1)
时间复杂度:
最好情况下:原本有序:比较次数n-1,交换次数0,O(n)
最坏情况下:全部逆序:比较次数=(n-1)+(n-2)+…+1=n(n-1)/2=交换次数,O(n^2)

4.快速排序

在待排序表[1…n]中任取一个元素pivot作为基准(通常取首元素),通过一趟排序将待排序表划分为独立的两部分[1…k-1][k+1…n],使[1…k-1]中所有元素小于pivot,[k+1…n]中的所有元素大于等于pivot,pivot放在(k)上,不断地进行划分,然后递归地对两个子表重复上面过程,直到每部分只有一个元素或空为止,即所有元素都放在最终位置上。

public int partition(int A[],int low,int high){
	int pivot=A[low];  //第一个元素作为基准
	while(low<high){ //用low、high搜索基准的最终位置
		while(low<high && [high]>=pivot){
			--high;
		}
		A[low]=A[high];//比基准大的元素移动到左端
		while(low<high&& A[low]<=pivot){
			++low;
		}
		A[high]=A[low];//比基准大的元素移动到右端
	}
	A[low]=pivot; //基准元素存放到最终位置
	return low; //返回存放基准元素的最终位置
}
快速排序
public void QuickSort(int A[],int low,int high){
	if(low<high){  //递归跳出来的条件
		int pivotPos=Partition(A,low,high);//划分
		QuickSort(A,low,pivotPos-1); //划分左子表
		QuickSort(A,pivotPos+1,high);//划分右子表

	}
}

时间复杂度O(n*递归层数)
最好:O(nlog2 n) 最坏:O(n^2)
空间复杂度O(递归层数)
最好:O(log2 n) 最坏:O(n)

二叉树的层数就是递归调用的层数

算法主要取决于递归深度,每次划分越均匀,则递归深度越低

如果每次选中的基准将待排序序列划分为很不均衡的两个部分,则会导致递归深度增加,算法效率变低
如果初始序列有序或者逆序,则快速排序的性能最差(因为每次选择都是最靠边元素)
快速排序算法优化思路:
尽量选择可以把数据中分的枢轴元素,比如:选头、中、尾三个位置的元素,取中间值作为枢轴元素;随机选一个元素作为枢轴(基准)元素

不稳定算法


5.简单选择排序

选择排序可分为简单选择排序和堆排序
每一趟在待排序元素中选取关键字最小的元素加入有序子队列
在后面未排序的元素中找到最小的元素,放到前面已经排序好的子队列中

public void SelectSort(int A[],int n){
	for(int i=0;i<n-1;i++){ //进行n-1趟
		int min=i; //记录最小元素的位置
		for(int j=i+1;j<n;j++){ //在A[i...n-1]中选择最小的元素
			if(A[j]<A[min]){
				min=j; //更新最小元素位置
			}
			if(min != i){
				int tmp=A[i];
				A[i]=A[min];
				A[min]=tmp;
			}
		}
	}
}

空间复杂度 O(1)
时间复杂度O(n^2)
无论有序、逆序、还是乱序、一定需要n-1趟处理
总共需要对比关键字n(n-1)/2次,元素交换次数<n-1
不稳定的算法
既可以用于顺序表,也可以用于链表

6.堆排序

大根堆 L(i)>=L(2i),L(i)>=L(2i+1); 根节点>=左、右孩子
小根堆 L(i)><=L(2i),L(i)<=L(2i+1); 根节点<=左、右孩子
每一趟将堆顶元素加入有序子序列(与待排序序列中的最后一个元素交换)(堆顶元素和堆底元素互换)
大根堆:检查当前节点是否满足根>=左、右孩子,不满足时,将当前节点与更大的一个孩子互换

//建立大根堆
public void buildMaxHeap(int A[],int n){ // n是元素的总个数
	for(int i=n/2;i>0;i--){ //从后往前调节所有非叶子结点 len/2后面的元素都是叶子结点
		HeadAdjust(A,i,n);
	}
} 

//将以k为根的子树调整为大根堆(下坠调整)
public void HeadAdjust(int A[],int k,int n){
	//把当前要处理的值放在A[0]位置,防止被覆盖
	A[0]=A[k];
	//i=2*k就是让它指向当前结点的左孩子
	for(int i=2*k;i<=n;i*=2){
		// A[i]和A[i+1] 就是比较当前结点的左右孩子谁更大
		if(i<n && A[i]<A[i+1]){
			i++;
		}
		if(A[0]>=A[i]){
			break; //筛选结束
		}else{
			A[k]=A[i];//将A[i]调整到双亲结点上
			k=i;//修改k值,便于继续向下筛选(小元素下坠 )
		}
	}
	A[k]=A[0];//被筛选结点的值放入最终位置
}
//交换
public void swap(int a,int b){
	int tmp=a;
	a=b;
	b=tmp;
}

//堆排序完整逻辑
public void HeapSort(int A[],int k,int n){
	//建立大根堆  O(n)
	buildMaxHeap(A,n);
	//n-1趟的交换和建堆过程
	for(int i=len;i>1;i--){
		//堆顶元素和堆底元素互换
		swap(A[i],A[1]);
		//把剩下的待排序元素整理成堆
		HeadAdjust(A,1,i-1);
	}
}

时间复杂度:
建堆的过程,关键字对比次数不超过4n,建堆时间复杂度O(n)
根节点最多下坠h-1层,每下坠一层,最多只需对比关键字2次,每一趟时间复杂度不超过 O(log2 n)
共n-1趟,总的时间复杂度为O(nlog2 n)
所以总的时间复杂度为O(n)+O(nlog2 n)=O(nlog2 n)
空间复杂度O(1)
不稳定的算法
基于大根堆排序得到的是递增序列,基于小根堆排序得到的是递减序列

在堆中插入新元素:

比如小根堆:
对于小根堆,新元素放到表尾,与父节点进行对比,若新元素比父节点更小,则将二者交换,新元素就这样一直“上升”,到无法上升为止。每次上升只需要对比关键字1次(与父节点进行对比)

在堆中删除一个元素:

比如小根堆:
删除一个元素后,将堆底的元素放在这个被删除元素的位置,然后让该元素下坠,直到无法下坠为止。
每次下坠可能需要进行比较关键字两次或者一次(比较两次:左右孩子对比一次,选出最小的那个孩子,再由该下坠元素和最小的孩子进行对比,如果比最小的孩子小,就和最小的孩子交换位置)

7.归并排序

把两个或者多个已经有序的序列合并成一个
m路归并,每选出一个元素需要对比关键字m-1次

public merge(int A[],int low,int mid,int high){
	int[] B=new int[A.length+1];
	int i=0,j=0,k=0;
	for(k=low;k<=high;k++){
		//将A中所有元素复制到B中
		B[k]=A[k];
	}
	//归并
	for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++){
		if(B[i]<=B[j]){
			A[k]=B[i++]; //将较小的值复制到A中
		}else{
			A[k]=B[j++];
		}
	}
	//没有归并完的部分复制到尾部
	while(i<=mid){
		A[k++]=B[i++];
	}
	while(j<=high){
		A[k++]=B[j++];
	}
}
public void MergeSort(int A[],int low,int high){
	if(low<high){
		int mid=(low+high)/2; //从中间划分
		MergeSort(A,low,mid);
		MergeSort(A,mid+1,high);
		Merge(A,low,mid,high);//归并
	}
}

时间复杂度:
每趟归并时间复杂度为O(n),则算法的时间复杂度为O(nlog2 n)
空间复杂度:
O(n) (使用辅助数组O(n),递归占用空间O(log2 n),所以主要受数组占用空间影响)
稳定的算法

8.基数排序

将关键字拆分为d位
以个位、十位、百位…大小进行排序
基数排序通常基于链式存储实现
r是基数(比如0~10)
需要r个辅助队列,空间复杂度为O®
一趟分配O(n),一趟收集O®,总共d趟分配,收集,时间复杂度O(d(n+r))
稳定的算法

9.外部排序

数据元素太多,无法一次全部读入内存进行排序
外部(外存,磁盘)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/370633.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

量子态操作:基于门的三态旋转

R-旋转操作 在量子计算中&#xff0c;RX, RY, RZ门是三种基本的单量子比特旋转门&#xff0c;它们分别绕X轴、Y轴、Z轴旋转量子比特的态矢量。 RX旋转门&#xff1a;绕X轴旋转角度为θ\thetaθ的RX门的矩阵表示为&#xff1a; Rx(θ)(cosθ2−isinθ2−isinθ2cosθ2)R_x(\th…

Python3+Selenium3自动化测试

此前对网页内容进行元素定位的操作&#xff0c;接下来就可以对已经定位的元素进行操作了&#xff0c;一般情况下定位好元素后通过IDE的提示就可以了解到有哪些方法 #coding utf-8 import time from selenium import webdriver from selenium.webdriver.common.by import By dr…

JAVA多线程牌号系统开发与应用

技术&#xff1a;Java等摘要&#xff1a;牌号系统是为解决一些服务业营业大厅排队问题而设计的&#xff0c;它能够有效地提高工作人员的工作效率&#xff0c;也能够使顾客合理的安排等待时间&#xff0c;让顾客感到服务的公平公正。论文首先讨论了牌号系统的背景、意义、应用现…

力扣-订单最多的客户

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;586. 订单最多的客户二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.其他总…

算法模板总结(自用)

算法模板总结滑动窗口双指针算法数组相关合并两个有序数组左右指针技巧快慢指针技巧字符串相关左右指针反转字符串问题快慢指针替换空格字符问题链表相关快慢双指针删除链表的倒数第N个节点链表相交环形链表链表操作几数之和两数之和四个数组的四数之和三数之和同一数组中四数之…

(十)守恒律(主平衡原理)

守恒律表示了物体中某些物理场量之间的关系&#xff0c;它可表述为&#xff1a;某一时刻 ttt&#xff0c;对于物理场量 Φ\bold\PhiΦ 在物体体积 vvv 上的物质积分&#xff0c;其时间变化率等于另一物理场量 Ψ\bold\PsiΨ(源) 在该体积上的物质积分&#xff0c;与通过物体表面…

AI高效学习路径总结

最近chatgpt爆红&#xff0c;百度近日也官宣了大模型新项目“文心一言”&#xff08;英文名ERNIE Bot&#xff09;&#xff0c;即将在今年3月面向公众开放&#xff0c;我先期待一波&#xff0c;到时候给同学们说说感受。 这也不是第一次人工智能引起广泛关注了&#xff0c;随着…

外贸人员如何让远程办公和在办公室一样办公

俗话说&#xff1a;“工欲善其事&#xff0c;必先利其器”。作为外贸人员&#xff0c;常用的工具主要分为三大类&#xff1a;客户管理工具、翻译工具、邮箱工具。往往外贸人员会忽略另一类工具&#xff1a;远程类工具。客户管理工具 有了客户管理工具&#xff0c;企业和员工都可…

软件测试之正交法

正交法 通过分析我们发现,对于图中的程序而言,我们要设计81条测试用例,那么有没有一种方法能够使用最小的测试过程集合获得最大的测试覆盖率呢? 1. 概述 1.1 定义 正交法,也叫正交实验法或者正交排列法, 就是使用最小的测试过程集合获得最大的测试覆盖率。 “正交实验”是…

LINUX应用GUI开发C++ 之gtkmm4(1)

目录概述GTKgtkmm安装gtkmm4hello,worldcodelite配置代码解释概述 GTK GTK是一个小部件工具包。GTK创建的每个用户界面都由小部件组成。这是在C语言中使用GObject实现的&#xff0c;GObject是一个面向对象的C语言框架。窗口小部件是主容器。然后通过向窗口中添加按钮、下拉菜…

Android Studio中创建java工程

1. 前言 电脑环境: Ubuntu 18.04 开发工具环境:Android Studio 4.1.3 版本 经常要使用验证Java API, 把配置环境步骤记录一下 2. 创建步骤 2.1 新建一个Android Studio App工程 New ---> New Project ---> 选择一个Activity主题---> Finish 就创建ok 2.2 …

如何防护DDOS攻击

DDOS攻击的趋势呈现出明显的增长趋势&#xff0c;特别是在网络攻击技术的发展和攻击者的技术水平提高的情况下&#xff0c;DDOS攻击的发生频率和规模也在不断增加。此外&#xff0c;随着网络技术的发展&#xff0c;攻击者也可以利用更多的技术手段来实施DDOS攻击&#xff0c;比…

kali环境搭建

一、渗透为什么要使用kali&#xff1f; 1、系统开源 kali linux实际上是开源的操作系统&#xff0c;其中内置了几百种工具而且是免费的&#xff0c;可以非常方便的为测试提供上手即用的整套工具&#xff0c;而不需要繁琐的搭建环境&#xff0c;及收集工具下载安装等步骤 2、系统…

通过OpenAI来做机械智能故障诊断-测试(1)

通过OpenAI来做机械智能故障诊断 1. 注册使用2. 使用案例1-介绍故障诊断流程2.1 对话内容2.2 对话小结3. 使用案例2-写一段轴承故障诊断的代码3.1 对话内容3.2 对话小结4. 对话加载Paderborn轴承故障数据集并划分4.1 加载轴承故障数据集并划分第一次测试4.2 第二次加载数据集自…

【华为OD机试模拟题】用 C++ 实现 - 挑选字符串(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 货币单位换算(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 选座位(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 停车场最大距离(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 重组字符串(2023.Q1) 【华为OD机试模…

浅谈对于封装、继承与多态的理解(Java)

目录 封装 继承 父类成员的访问 如何在子类中访问父类同名的成员 子类构造方法 代码块在继承上的执行顺序 多态 多态的实现条件 重写 多态的优缺点 面向对象程序的三大特征&#xff1a;封装继承和多态。 封装 封装&#xff1a;将数据和操作数据的方法进行有效…

前置知识-辛几何与辛代数、欧式几何与辛几何、Hamilton量

1.3 Hamilton 系统的辛几何算法 1.3.1 辛几何与辛代数的基本概念(下篇) 定义(辛空间、辛映射) 设 V V V 是定义在实数域 R \mathbb{R} R<

【万能排序之qsort、b_sort 、s_sort】

文章目录前言:star:qsort函数函数参数qsort函数的使用:star:模拟实现万冒泡排序函数参数模拟实现b_sort注意点:star:模拟实现万能选择排序函数参数模拟实现s_sort最后前言 我们所熟悉的冒泡排序&#xff0c;选择排序&#xff0c;插入排序&#xff0c;二分排序等都是基于给定的一…

计算机网络笔记、面试八股(一)——TCP/IP网络模型

Note&#xff1a;【计算机网络笔记、面试八股】系列文章共计5篇&#xff0c;现已更新3篇&#xff0c;剩余2篇&#xff08;TCP连接、Web响应&#xff09;会尽快更新&#xff0c;敬请期待&#xff01; 本章目录1. TCP/IP网络模型1.1 应用层1.1.1 应用层作用1.1.2 应用层有哪些常用…

git push origin master 情况

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3;哈喽&#xff01;大家好&#xff0c;我是「奇点」&#xff0c;江湖人称 singularity。刚工作几年&#xff0c;想和大家一同进步&#x1f91d;&#x1f91d;一位上进心十足的【Java ToB端大厂领…