数据结构与算法—排序算法篇

news2024/11/27 9:38:39

目录

1、选择排序

1.1、算法思想

1.2、步骤

1.3、算法实现

1.4、算法分析

2、 冒泡排序

2.1、算法思想

2.2、算法实现

2.3、算法分析

2.4、改进冒泡排序

3、插入排序

3.1、算法思想

3.2、算法实现

3.3、算法分析

4、希尔排序

4.1、算法思想

4.2、增长量选定规则

4.3、算法实现

4.4、算法分析

5、归并排序

5.1、算法思想

5.2、算法实现

5.3、算法分析

6、快速排序

6.1、算法思想

6.2、算法实现

6.3、算法分析

7、基数排序(桶排序)

7.1、算法思想

7.2、算法步骤

7.3、算法实现

7.4、算法分析

8、堆积树排序

8.1、算法思想


1、选择排序

1.1、算法思想

每趟从待排序的数据元素中,选出最小(或最大)的一个元素,顺序放在待排序的数列最前面,直到全部待排序的数据元素排完。

1.2、步骤

1、查找此数列中的最小元素,将其与第一个交换

2、从第二个值开始找,查找到最小元素,将其与第二个交换

3、以此类推,直到遍历结束

1.3、算法实现

//选择排序
void Select_Sort(int a[],int n) {	//第一个参数是数组,第二个参数是 元素个数
	for (int i = 0; i < n;i++) {
		for (int j = i + 1; j < n;j++) {
			if (a[i] > a[j]) {	//a[i] > a[j] 就是升序 ,< 为降序
				int temp;
				temp = a[i];
				a[i] = a[j];
				a[j] = temp;
			}
		}
	}

}

1.4、算法分析

1、时间复杂度:O(n2)

2、空间复杂度:O(1)

3、会改变数据排列顺序,是不稳定排序

4、适用于数据量小,或有部分数据已经排序的情况

2、 冒泡排序

2.1、算法思想

1、比较相邻的元素,如果前一个元素比后一个元素大,就交换两个元素的位置。

2、对每一对相邻元素作相同的工作,从开第一对元素到结尾最后一对元素,最终最后位置的元素就是最大值。

2.2、算法实现

//冒泡排序
void bubble_sort(int a[], int n) {
	for (int i = 0; i < n; i++) {
		for (int j = 1; j < n - i;j++) {
			if (a[j-1] > a[j]) {	//  > 升序, < 降序
				int temp;
				temp = a[j-1];
				a[j-1] = a[j];
				a[j] = temp;
			}
		}
		
	}
}

2.3、算法分析

1、时间复杂度:O(n2)

2、冒泡在比较两个相邻元素后,再决定是否互换位置,不会改变原来排列的顺序,因此是稳定排序算法。

3、空间复杂度:O(1)

4、冒泡排序法适合数据量小,或有部分数据已经排过序的情况。

2.4、改进冒泡排序

缺点:无论数据是否已排序完成都固定会执行n(n-1)/2次数据比较。

改进方法:加入一个判断条件来判断何时可以提前结束循环。

//改进冒泡排序
void bubble_sort1(int a[], int n) {
	for (int i = 0; i < n; i++) {
		/*
		引入一个标志,如果为0 则说明以下未发生交换,
		即,后面的数据已经有序。
		如果不等于 0 ,说明后面暂时无序,还需要比较
		*/
		int flag = 0;	
		for (int j = 1; j < n - i; j++) {
			if (a[j - 1] > a[j]) {	//  > 升序, < 降序
				int temp;
				temp = a[j - 1];
				a[j - 1] = a[j];
				a[j] = temp;
				flag++;
			}
		}
		if (flag == 0) {	/为 0 ,结束排序
			break;
		}

	}
}

3、插入排序

3.1、算法思想

1、把所有的元素分为两组,已经排序的和未排序的

2、找到未排序的组中的第一个元素,向已排序的组中进行插入;

3、倒序遍历已排序的元素,依次和待插入的元素进行比较,如果某个元素小于待插入元素,则交换两者位置。

3.2、算法实现

//插入排序
void insert_sort(int a[],int n) {
	for (int i = 1; i < n;i++) {
		int index = i;	//引入一个变量,记录当前待插入的元素索引
		for (int j = i-1; j >= 0;j--) {
			if (a[j] > a[index]) {	// > 升序   <降序
				int temp;
				temp = a[j];
				a[j] = a[index];
				a[index] = temp;
				index = j;	//交换后,需要更换索引的值,才能进行下一步比较
			}
		}
	}

}

3.3、算法分析

1、时间复杂度:O(n^2)

2、插入排序是稳定的排序

3、空间复杂度:O(1)

4、适用于大部分数据已经排序的情况,或者往已排序的数据库中添加新的元素,然后再次排序的情况。

5、插入排序会造成大量的数据迁移,建议在链表上使用。

4、希尔排序

4.1、算法思想

1、选定一个增长量h,按照增长量h作为数据分组的一句,对数据进行分组。

2、对分好组的每一组数据进行插入排序。

3、减小增长量,最小减为1,重复第二步操作。

4.2、增长量选定规则

对于初始增长量,没有固定的规则,很多论文研究了各种不同的递增序列,但都无法增量那个序列是最好的,这里简单介绍两种:
1、

int h = 1;
while(h < 数组长度 / 2){
    h = 2*h -1;
}

2、​ h = 数组长度 / 2;

每次减少量:​​ h = h /2;​

4.3、算法实现

//希尔排序
void shell_sort(int a[],int n) {	//1、数组首地址  2、数组长度
	//计算步长
	int jmp = 1;
	while (jmp < n >>1) {	 // jmp < n / 2   >>1 一定程度上等价于 /2
		jmp = jmp * 2 + 1;
	}

	//插入排序
	int tmp;	//记录当前待插入元素
	int j;		//定位比较的元素
	while (jmp != 0) {
		for (int i = jmp; i < n; i++) {
			tmp = a[i];
			j = i - jmp;
			while (j >= 0 && tmp < a[j]) {
				a[j + jmp] = a[j];
				j = j - jmp;
			}
			a[j + jmp] = tmp;
		}
		jmp = jmp / 2;
	}
}

4.4、算法分析

1、当初始步长 ​h = 数组长度 / 2 ​时 ,时间复杂度为O(n3/2)

2、稳定的排序算法

3、空间复杂度:O(1)

4、适用于大部分数据已经排序的情况

5、归并排序

5.1、算法思想

1、尽可能的将一组数据才分成两个等分的子组,并对每个子组进行拆分,直到每个子组的元素个数为1为止

2、将相邻的两个子组进行合并成一个有序的大组

3、重复步骤2,直到最终只有一个组为止。

5.2、算法实现

//归并排序
//2、归并 vector
void Merge(int a[], int left, int mid, int right) {
	vector<int> vt;		//定义一个临时动态数组
	vt.resize(right-left+1);	//指定vector尺寸
	int k = 0;
	int pl = left;
	int pr = mid + 1;

	//同时成立时,两个索引的值进行比较
	while (pl <= mid && pr <= right) {
		if (a[pl] < a[pr]) {
			vt[k++] = a[pl++];

		}
		else {
			vt[k++] = a[pr++];

		}

	}

	//如果是因为 pr 不满足条件,则将剩下的pl索引元素放入
	while (pl <= mid) {
		vt[k++] = a[pl++];
	}

	//如果是因为 pl 不满足条件,则将剩下的pr索引元素放入
	while (pr <= right) {
		vt[k++] = a[pr++];
	}

	//拷贝 临时 -> 目标
	for (int i = left, j = 0; i <= right; i++, j++) {
		a[i] = vt[j];
	}

}


//1、排序
void Merge_sort(int a[],int left,int right) {	//1、数组首地址,2、组的左边界,3、组的右边界
	//递归到子组只有一个元素时,退出
	if (left == right) {
		return;
	}

	//折半,划分子组
	int mid = (left + right) / 2;
	//左子组归并排序
	Merge_sort(a,left,mid);
	//右子组归并排序
	Merge_sort(a, mid+1,right);
	//排序后,进行归并
	Merge(a,left,mid,right);

}

5.3、算法分析

1、时间复杂度:O(nlog2n)

2、缺点:需要申请数组空间,导致空间浪费,是典型的空间换时间操作

3、归并排序是稳定排序法

6、快速排序

6.1、算法思想

1、在数据中找一个虚拟的中间件(一般就是第一个元素)

2、按照中间值,将小于中间值的数据放在中间值的左边,大于中间值的数据放在右边

3、再以同样的方式分别处理左右两边的数据,直到排完序为止。

6.2、算法实现

1、递归

//快速排序——递归
void quick_sort(int a[],int size, int left, int right) { //1、数组首地址 2、左边界 3、右边界	

	if (left >= right) {
		return;
	}
	//以a[left] 为基准
	int le = left+1;
	int ri = right;

	while(true){

		cout << "第" << ret++ << "次:";
		for (int i = 0; i < size; i++) {
			cout << a[i] << " ";
		}
		cout << endl;


		//从左->右,取大于
		while (le <= right) {
			if (a[le] >= a[left]) {
				break;
			}
			le++;
		}

		//从右->左,取小于
		while (ri >= left+1) {
			if (a[ri] <= a[left]) {
				break;
			}
			ri--;
		}

		//如果 左边索引 < 右边索引 ,交换le 和 ri索引的值
		if (le < ri) {
			int temp1;
			temp1 = a[le];
			a[le] = a[ri];
			a[ri] = temp1;
		}
		else {
			break;
		}
	}
	//当左边索引 >= 右边索引 ,将ri位置的元素和base 交换
	if (le >= ri) {
		int temp;
		temp = a[left];
		a[left] = a[ri];
		a[ri] = temp;


		quick_sort(a,size,left,ri-1);
		quick_sort(a,size,ri+1,right);
	}
}

2、非递归—利用栈,代替递归

// 子组排序算法
int get_key(int a[],int left, int right) {

	//取基准为 a[left]
	int lf1 = left+1;
	int rg1 = right;

	while (true) {	//控制比较躺数
		//左 -> 右  
		while (lf1 <= right) {
			if (a[lf1] > a[left]) {
				break;
			}
			lf1++;
		}

		//右 -> 左  
		while (rg1 >= left + 1) {
			if (a[rg1] < a[left]) {
				break;
			}
			rg1--;
		}

		if (lf1 < rg1) {
			int temp = a[lf1];
			a[lf1] = a[rg1];
			a[rg1] = temp;
		}
		else {
			break;
		}
	}
	if (lf1 >= rg1) {
		int tmp = a[left];
		a[left] = a[rg1];
		a[rg1] = tmp;
	}

	return rg1;

}

//快速排序——非递归 栈
void quick_sort_no(int a[],int size, int left, int right) {
	if (left>=right) {
		return;
	}

	stack<int> s;
	s.push(left);
	s.push(right);

	while (!s.empty()) {
		int rg = s.top();
		s.pop();
		int lf = s.top();
		s.pop();

		int key = get_key(a,lf, rg);

		cout << "第" << ret++ << "次:";
		for (int i = 0; i < size; i++) {
			cout << a[i] << " ";
		}
		cout << endl;

		if (key-1 > lf ) {
			s.push(lf);
			s.push(key-1);
		}
		if (key+1 < rg) {
			s.push(key+1);
			s.push(rg);
		}
	}
}

3、非递归—利用队列,代替递归

// 子组排序算法
int get_key(int a[],int left, int right) {

	//取基准为 a[left]
	int lf1 = left+1;
	int rg1 = right;

	while (true) {	//控制比较躺数
		//左 -> 右  
		while (lf1 <= right) {
			if (a[lf1] > a[left]) {
				break;
			}
			lf1++;
		}
		  
		//右 -> 左  
		while (rg1 >= left + 1) {
			if (a[rg1] < a[left]) {
				break;
			}
			rg1--;
		}

		if (lf1 < rg1) {
			int temp = a[lf1];
			a[lf1] = a[rg1];
			a[rg1] = temp;
		}
		else {
			break;
		}
	}
	if (lf1 >= rg1) {
		int tmp = a[left];
		a[left] = a[rg1];
		a[rg1] = tmp;
	}

	return rg1;

}

//快速排序——非递归 队列
void quick_sort_no01(int a[], int size, int left, int right) {
	if (left >= right) {
		return;
	}

	queue<int> q;
	q.push(left);
	q.push(right);

	while (!q.empty()) {
		int lf = q.front();
		q.pop();
		int rg = q.front();
		q.pop();

		int key = get_key(a, lf, rg);

		cout << "第" << ret++ << "次:";
		for (int i = 0; i < size; i++) {
			cout << a[i] << " ";
		}
		cout << endl;

		if (key - 1 > lf) {
			q.push(lf);
			q.push(key - 1);
		}
		if (key + 1 < rg) {
			q.push(key + 1);
			q.push(rg);
		}


	}
}

6.3、算法分析

1、最好和平均情况下,时间复杂度O(nlog2n),最坏,O(n^2)

2、快速排序不是稳定的排序算法

3、最好情况下,空间复杂度O(log2n),最坏情况下,O(n)

4、快速排序算法是平均速度最快的算法。

7、基数排序(桶排序)

7.1、算法思想

从数据的低位(LSD)或者高位 (MSD) 开始,按照这一位数字的值(0~9)放到对应的编号为 0~9 的桶中。我们按照这一位数字进行完了排序,再按照次低位(下一位)数字进行上述的操作。重复这样的操作一直到最高位,直到最后每一位数字都进行完排序,整个基数排序完成。

7.2、算法步骤

1、建立一个桶,存放每个位数上出现元素的个数

2、最低位开始,将数列按照该位的数值放入到桶中,

3、将桶中的存放的数据按 a[i+1] = a[i],依次相加,计算出当前数字的位置。

4、把数列按照在当前桶中的顺序依次存放到临时数组中

5、将临时数组的值,拷贝到原数组中

6、依次循环,直到最高位比较结束,排序完成。

7.3、算法实现

//基数排序(桶排序)
void radix_sort(int* a,int size) {

	//找出数组中的最大值(顺序查找)
	int max = a[0];
	for (int i = 1; i < size;i++) {
		if (a[i] > max) {
			max = a[i];
		}
	}
	
		//定义一个桶,存放每个位数,出现的元素次数
	int base = 1;
	int* tmp = (int*)malloc(sizeof(int) * size);	//开辟一块连续的地址,作为临时数组
	if (tmp) {
		while (max / base > 0) {
			int data[10] = { 0 };
			//记录元素在每个位数上,出现的次数
			for (int i = 0; i < size; i++) {
				data[(a[i] / base) % 10]++;
			}

			//将桶中的数据,变为需要存放在临时数组的位置
			for (int i = 1; i < 10; i++) {
				data[i] += data[i - 1];
			}

			for (int i = size - 1; i >= 0; i--) {
				tmp[data[(a[i] / base) % 10] - 1] = a[i];
				data[(a[i] / base) % 10]--;
			}
			for (int i = 0; i < size; i++) {
				a[i] = tmp[i];
			}

			base = base * 10;
		}
	}
	free(tmp);	
}

7.4、算法分析

1、时间复杂度:O(nlog​​p​​k)

2、基数排序是稳定排序法

3、空间复杂度:O(n*p),n是原始数据个数,p是数据字符数

4、如果n很大,p固定或者很小,此排序效率很高。

8、堆积树排序

8.1、算法思想

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

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

相关文章

Auto-GPT 迈向智能体的第一步——从信息增强和上下文理解开始

Auto-GPT 的出现&#xff0c;意味着 AI 已经能够在没有人工干扰的情况下独立地完成目标任务。这个在 GitHub 中不断创造历史的项目&#xff0c;正以惊人的速度发展着、变化着。 这样前沿又带有科幻色彩的技术项目&#xff0c;引起了各方关注&#xff0c;开发者、投资人、媒体人…

《操作系统》by李治军 | 实验5.pre - switch_to 汇编代码详解

目录 【前言】 一、栈帧的处理 1. 什么是栈帧 2. 为什么要处理栈帧 3. 执行 switch_to 前的内核栈 4. 栈帧处理代码分析 二、PCB 的比较 1. 根据 PCB 判断进程切换与否 2. PCB 比较代码分析 三、PCB 的切换 1. 什么是 PCB 的切换 2. PCB 切换代码分析 四、TSS 内核…

ChatGPT再起争端,如何应对未来的机器挑战?速来学习解决方法!

AI孙燕姿事件 前些日子&#xff0c;“AI孙燕姿”一度火爆出圈。其实就是有网友使用人工智能技术&#xff0c;利用神似歌星孙燕姿的声音翻唱了不少歌手的代表作&#xff0c;并上传到社交平台上&#xff0c;而其翻唱作品的逼真程度是“甚至换气声都可以模仿”。随着“AI孙燕姿”翻…

你还不会AVL树吗?

AVL树 AVL树概念AVL树的插入结点定义插入流程左单旋右单旋左右双旋右左双旋 验证AVL树 AVL树概念 &#x1f680;AVL树是一颗平衡的二叉搜索树&#xff0c;所谓平衡是指左右子树的高度差的绝对值不超过1。所以一颗AVL树&#xff08;如果不是空树&#xff09;有以下性质&#xf…

2023 年的 Web Worker 项目实践

目录 前言 引入 Web Worker Worker 实践 Worker 到底有多难用 类库调研 有类库加持的 worker 现状 向着舒适无感的 worker 编写前进 1. 抽取依赖&#xff0c;管理编译和更新&#xff1a; 2. 定义公共调用函数&#xff0c;引入所打包的依赖并串联流程&#xff1a; 3. …

两种方法绘制笑脸(需要用到canvas标签)

两种方法绘制笑脸&#xff08;需要用到canvas标签&#xff09; 方法一&#xff1a; <!DOCTYPE html> <html><head lang"en"><meta charset"utf-8"><title>绘制笑脸-方法一</title></head><body><canv…

二开项目权限应用全流程

二开项目-权限应用全流程(人力资源类) addRoutes基本使用** 格式 **router.addRoutes([路由配置对象])**或者:this.$router.addRoutes([路由配置对象]) 改造代码 1 .在router/index.js中的路由配置中删除动态路由的部分 export const constantRoutes [{path: /login,comp…

【ChatGPT】如何入门GPT并快速follow当前的大语言模型LLM进展?

如何入门GPT并快速follow当前的大语言模型LLM进展? 自从去年chatGPT悄悄发布,OpenAI发布的GPT系列工作也变得炙手可热,而基于此,各家公司/实验室百家争鸣,纷纷发布自己的工作,可以说每天都有新的进展。 在当前的情况下,要如何入门GPT系列生成模型,并快速跟进SOTA进展…

SpringMVC一站式学习,分分钟钟让你上手

文章目录 一、SpringMVC1.1 引言1.2 MVC架构1.2.1 概念1.2.2 好处 二、开发流程2.1 导入依赖2.2 配置核心(前端)控制器2.3 后端控制器2.4 配置文件2.5 访问 三、接收请求参数3.1 基本类型参数3.2 实体收参【重点】3.3 数组收参3.4 集合收参 【了解】3.5 路径参数3.6 中文乱码 四…

突破障碍:数字化如何改变对外劳务行业

有没有一份工作是又高薪又能学英语又能环游世界&#xff1f;在小红书上一搜&#xff0c;就发现许多年轻人曾经有过“国际邮轮”工作的经历&#xff0c;打卡全球100城市、全方面的英文口语环境、一觉起来就是一个新的国家...而且还能赚钱&#xff1f;听上去真是令人向往的生活&a…

PoseiSwap 参赛,参与斯坦福、Nautilus Chain等联合主办的 Hackathon 活动

近日&#xff0c;由 Stanford Blockchain Accelerator、Zebec Protocol、 Nautilus Chain、Rootz Lab 共同主办的“ Boundless Hackathon Stanford ” 主题的黑客松活动&#xff0c;目前已接受报名。该活动旨在帮助更多的优质开发者参与到 Web3 世界的发展中&#xff0c;推动链…

自动化测试框架、Python面向对象以及POM设计模型简介

目录 1 自动化测试框架概述 2 自动化测试框架需要的环境 3 自动化测试框架设计思想&#xff1a;Python面向对象 4 自动化测试框架设计思想&#xff1a;POM&#xff08;Page Object Model&#xff09;页面对象模型 1 自动化测试框架概述 所谓的框架其实就是一个解决问题…

如何在华为OD机试中获得满分?Java实现【去除多余空格】一文详解!

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Java华为OD机试真题(2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述4. Java算法源码5. 测试6.解题思路1. 题目描述 去除文本多余空格,但不…

MOTOTRBO CPS2.0安装与写频流程

一、安装MOTOTRBO CPS2.0写频软件 安装MOTOTRBO CPS2.0写频软件&#xff0c;选择安装软件的电脑系统必须WIN7以上 1.解压CPS2_2.21.61.0.zip至当前文件内 2. 双击MOTOTRBO_CPS_2.0.exe安装文件 3. 选择安装语言中文&#xff08;简体&#xff09;&#xff0c;点击确定 4.点击下一…

SW质量属性

1. 覆盖质量属性&#xff1a;指派质量、质量中心和惯性张量的值以覆写所计算的值。 2. 质量属性内容&#xff1a; 密度质量体积曲面区域质量中心惯性主轴惯性矩和产品准则 在图形区域中&#xff0c;单色三重轴指示了模型的主轴和质量中心。 三色参考 3D 三重轴将显示在原点 …

Windows10如何快速安装虚拟机! Hyper-V

您可以在 Windows 10 上使用 Hyper-V 来创建虚拟机。Hyper-V 是 Microsoft 提供的虚拟化软件。您可以按照以下步骤在 Windows 10 上安装 Hyper-V 虚拟机&#xff1a; 1. 打开 Hyper-V 管理器&#xff0c;您可以按下 Windows 键并键入 “Hyper-V 管理器”。如果没有Hyper-V这个选…

汇编寄存器认识

1.8086CPU的16个寄存器: 8086CPU所有寄存器都16位: 通用寄存器: 存放一般性数据: 包括 数据寄存器 , 指针寄存器, 索引寄存器 (AX,BX,CX,DX,BP,SP,SI,DI) 数据寄存器: AX,BX,CX,DX AX: AX(Accumulator Register) &#xff1a;累加寄存器&#xff0c;主要用于输入/输出和大…

【JavaSE】Java基础语法(十七)

文章目录 1. final2. 代码块2.1 代码块概述2.2 代码块分类 1. final fianl关键字的作用 final代表最终的意思&#xff0c;可以修饰成员方法&#xff0c;成员变量&#xff0c;类 final修饰类、方法、变量的效果 fianl修饰类&#xff1a;该类不能被继承&#xff08;不能有子类&a…

【EHub_tx1_A200】Ubuntu18.04 + ROS-Melodic/ROS2-Elequent + 速腾 RS-Helios_16P雷达 评测

大家好&#xff0c;我是虎哥&#xff0c;之前使用了很多单线激光雷达&#xff0c;这几年&#xff0c;3D激光雷达国产化后&#xff0c;逐步已经降价很多&#xff0c;3D激光雷达对于大环境导航&#xff0c;无人驾驶辅助导航&#xff0c;都有很多优势。经过逐步的筛选&#xff0c;…

性能测试知多少---性能分析与调优的原理

最近一直纠结性能分析与调优如何下手&#xff0c;先从硬件开始&#xff0c;还是先从代码或数据库。从操作系统&#xff08;CPU调度&#xff0c;内存管理&#xff0c;进程调度&#xff0c;磁盘I/O&#xff09;、网络、协议&#xff08;HTTP&#xff0c; TCP/IP &#xff09;&…