【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析

news2024/12/23 17:37:39

前言:

        接上篇,排序算法除了选择排序(希尔排序)和插入排序(堆排序)之外,还用交换排序(冒泡排序、快速排序)和归并排序已经非比较排序,本篇来深层解析这些排序算法

一、交换排序

        1.1、冒泡排序

        冒泡排序,这个再熟悉不过了,学校中老师讲的第一个排序就是冒泡排序;直接看代码

代码如下:

//冒泡排序
void BubbleSort(int* arr, int n)
{
	int exchange = 0;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				exchange = 1;
				Swap(&arr[j], &arr[j + 1]);
			}
		}
		if (exchange == 0)
		{
			break;
		}
	}
}

        时间复杂度:O(n^2);空间复杂度O(1)。

        1.2、快速排序

        快速排序,是hoare于1962年提出的一种二叉树结构的交换排序算法,其基本思想为:任意取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两个子序列,左子序列中所有元素均小于基准值,右子序列所有元素均大于基准值然后左右子序列重复此过程,直到所有元素都排列在相应位置上。

实现快速排序主要框架如下:

//快速排序
void QuickSort(int* a, int left, int right)
{
	if (left >= right) {
		return;
	}
	//_QuickSort⽤于按照基准值将区间[left,right)中的元素进⾏划分
	int meet = _QuickSort(a, left, right);
	QuickSort(a, left, meet - 1);
	QuickSort(a, meet + 1, right);
}

下面的不同方法指的是找基准值的方法不同而已。

1.2.1、hoare版本

思路:

        1.  创建左右指针,却基准值

        2.  从左到右找出比基准值大的数据,从右到左找出比基准值小的数据,左右指针数据交换,进入下一次循环

这里可能有一些问题,

        1.跳出循环后,right位置的值一定不大于key?

        当left > right时,即right走到了left的左侧,而left走过的位置值都不大于key,因此right此时指向的数据一定不大于key

        2.为什么left或者right指定的数据与key值相等也要交换?

        这里如果,数组中大量的数据都相等,不进行交换的话,就无法进行有效的分割数组。

代码实现:

//快速排序
int _QuickSort1(int* arr, int left, int right)
{
	int key = arr[left];
	int mid = left;
	left++;
	while (left <= right)
	{
		//左边找大
		while (left<=right && arr[left] < key)
		{
			left++;
		}
		//右边找小
		while (left <= right && arr[right] > key)
		{
			right--;
		}
		if (left <= right)
		{
			Swap(&arr[left], &arr[right]);
			left++;
			right--;
		}
	}
	Swap(&arr[mid], &arr[right]);
	return right;
}

1.2.2、挖坑法

思路:

        创建左右指针;首先从右向左找出比基准值小的数据,找到后立即放入左边 "坑" 中,当前位置变为新的 "坑",然后从左往右找出比基准值大的数据,找到后立即放入右边坑中,当前位置变为新的 "坑",结束循环后将最开始存储的分界值放入当前的 "坑"中,返回当前"坑"下标。

代码实现如下:

int _QuickSort2(int* arr, int left, int right)
{
	int tmp = arr[left];
	int hole = left;
	left++;
	while (left < right)
	{
		//从右边开始找
		while (left < right && arr[right] > tmp)
		{
			right--;
		}
		arr[hole] = arr[right];
		hole = right;
		while (left < right && arr[left] < tmp)
		{
			left++;
		}
		arr[hole] = arr[left];
		hole = left;
	}
	arr[hole] = tmp;
	return hole;
}

1.2.3、lomuto指针法

思路:

        创建前后指针,从左往右找比基准值小的进行交换,使得小的都排在基准值的左边。

代码实现:

int _QuickSort(int* arr, int left, int right)
{
	int prev = left, pcur = left + 1;
	int key = left;
	while (pcur <= right)
	{
		if (arr[pcur] < arr[key] && ++prev != pcur)
		{
			Swap(&arr[prev], &arr[pcur]);
		}
		pcur++;
	}
	Swap(&arr[key], &arr[prev]);
	return prev;
}

二、归并排序

思路:

        归并排序,是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个典型的应用。将已有序的子序列合并,得到完全有序的序列;即先让每一个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路合并。

代码实现:

//归并排序
void _MergeSort(int* arr, int left, int right,int* tmp)
{
	if (left >= right)
	{
		return;
	}

	int mid = (left + right) / 2;
	_MergeSort(arr, left, mid, tmp);
	_MergeSort(arr, mid+1, right, tmp);


	//合并数组
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] < arr[begin2])
		{
			tmp[index++] = arr[begin1++];
		}
		else {
			tmp[index++] = arr[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		tmp[index++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[index++] = arr[begin2++];
	}
	//将tmp数据拷贝回arr中
	for (int i = left; i <= right; i++)
	{
		arr[i] = tmp[i];
	}
}
void MergeSort(int* arr, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);

	_MergeSort(arr, 0, n - 1, tmp);

	free(tmp);
}

三、非比较排序

非比较排序,就是不进行比较数据来进行排序。

        计数排序

1>  统计相同元素出现次数

2>  根据统计的结果将序列回收到原来的序列中

这里又会存在一些问题,比如如果数据是负数,那该怎样开辟空间?

        这里,我们开辟空间大小为数组数据最大值和最小值之差。

然后在将统计结果返回到原来数组当中时,让数组下标加上原数组最小值即可。

//计数排序
void CountSort(int* arr, int n)
{
	int max = arr[0];
	int min = arr[0];
	for (int i = 0; i < n; i++)
	{
		if (arr[i] > max)
		{
			max = arr[i];
		}
		if (arr[i] < min)
		{
			min = arr[i];
		}
	}

	//开辟空间
	int range = max - min + 1;
	int* tmp = (int*)calloc(sizeof(int),range);
	if (tmp == NULL)
	{
		perror("calloc fail");
		return;
	}

	for (int i = 0; i < n; i++)
	{
		tmp[arr[i] - min]++;
	}
	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (tmp[i]--)
		{
			arr[j++] = i + min;
		}
	}
}

各种排序算法的算法复杂度和稳定性分析

到这里,排序算法就结束了,希望你能有所收获

感谢各位大佬支持并指出问题,

                        如果本篇内容对你有帮助,可以一键三连支持以下,感谢支持!!!

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

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

相关文章

2 卷积神经网络CNN

文章目录 LeNet-5AlexNetGoogLeNetResNet 本章代码均在kaggle上运行成功 LeNet-5 import torch import torch.nn as nn from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pyplot as plt from matplotlib_inline impo…

木马后门实验

实验拓扑 实验步骤 防火墙 配置防火墙—充当边界NAT路由器 边界防火墙实现内部 DHCP 分配和边界NAT需求&#xff0c;其配置如下 登录网页 编辑接口 配置e0/0 配置e0/1 编辑策略 测试&#xff1a;内部主机能获得IP&#xff0c;且能与外部kali通信 kali 接下来开启 kali 虚…

【视频讲解】后端增删改查接口有什么用?

B站视频地址 B站视频地址 前言 “后端增删改查接口有什么用”&#xff0c;其实这句话可以拆解为下面3个问题。 接口是什么意思&#xff1f;后端接口是什么意思&#xff1f;后端接口中的增删改查接口有什么用&#xff1f; 1、接口 概念&#xff1a;接口的概念在不同的领域中…

BUGKU-WEB-好像需要密码

如果点击start attrack 后出现 Payload set 1: Invalid number settings 的提示&#xff0c;先点hex 后点 decimal 再开始start attrack&#xff0c;这是一个软件bug&#xff0c;需要手动让它刷新。 解题思路 先随便输入测试&#xff1a;admin看看源码吧那就爆破了 据说&…

项目比赛经验分享:如何抓住“黄金一分钟”

项目比赛经验分享&#xff1a;如何抓住“黄金一分钟” 前言引起注意&#xff1a;用事实和故事开场明确痛点&#xff1a;描述问题和影响介绍解决方案&#xff1a;简明扼要激发兴趣&#xff1a;使用视觉辅助概述演讲结构&#xff1a;清晰的路线图我的开场白示例结语 前言 在创新的…

【小超嵌入式】 交叉编译工具安装过程

1、下载交叉编译工具链 ● 确定目标平台&#xff1a; 首先&#xff0c;你需要确定你的目标平台是什么&#xff0c;比如ARM、MIPS等。不同的目标平台需要不同的交叉编译工具链。 ● 获取工具链&#xff1a; 官方网站&#xff1a;通常可以从交叉编译器的官方网站下载适用于你的…

一番赏小程序开发,为消费者带来更多新鲜体验

一番赏作为经典的潮玩方式&#xff0c;深受消费者的喜爱&#xff0c;一番赏还会与不同的热门IP合作&#xff0c;不断推出新的赏品&#xff0c;吸引众多粉丝。赏品的内容非常丰富&#xff0c;从手办、公仔玩具等&#xff0c;还设有隐藏款和最终赏商品&#xff0c;对玩家拥有着非…

人工智能大模型发展的新形势及其省思

自2022年底OpenAI发布ChatGPT以来&#xff0c;大模型产业发展先后经历了百模大战、追求更大参数、刷榜竞分&#xff0c;直到近期各大厂商相继加入价格战&#xff0c;可谓热点纷呈。大模型的技术形态也从单纯文本发展到了多模态&#xff0c;从模拟人类大脑的认知功能发展到操控机…

暂存篇:高频面试题基本总结回顾(含笔试高频算法整理)

干货分享&#xff0c;感谢您的阅读&#xff01; &#xff08;暂存篇---后续会删除&#xff0c;完整版和持续更新见高频面试题基本总结回顾&#xff08;含笔试高频算法整理&#xff09;&#xff09; 备注&#xff1a;引用请标注出处&#xff0c;同时存在的问题请在相关博客留言…

韦东山瑞士军刀项目自学之分析部分GPIO_HAL库函数代码

GPIO_HAL部分库函数分析 主要是分析了宏定义&#xff0c;这些宏定义可以被写入到对应的寄存器之中&#xff0c;从引脚到GPIO组再到模式速度等等&#xff0c;每一个参数都对应着寄存器的一位或几位。以后自己还是根据库函数来开发吧&#xff0c;太麻烦了。

《浅谈如何培养树立正确的人工智能伦理观念》

目录 摘要&#xff1a; 一、引言 二、《机械公敌》的情节与主题概述 三、人工智能伦理与法律问题分析 1.伦理挑战 2.法律问题 四、培养正确的人工智能伦理观念的重要性 五、培养正确的人工智能伦理观念的途径与方法 1.加强教育与宣传 2.制定明确的伦理准则和规范 3.…

Java学习Day16:基础篇6

1.静态和非静态 2.调用静态和非静态的过程 注&#xff1a;在Java中&#xff0c;同类中&#xff0c;确实可以使用类的对象来调用静态方法&#xff0c;尽管这不是推荐的做法。静态方法属于类本身&#xff0c;而不是类的任何特定实例。因此&#xff0c;理论上讲&#xff0c;你应该…

分隔链表(LeetCode)

题目 给你一个链表的头节点 和一个特定值 &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 的节点都出现在 大于或等于 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 示例1&#xff1a; 输入&#xff1a;&#xff0c; 输出&#xff1a; 示例2&a…

七言-绝美崇州

题记 今天&#xff0c;2024年07月30日&#xff0c;在看到《今日崇州》 发布的航拍风光照片之后&#xff0c;这才方知笔者虽已寄居崇州“西川第一天”街子古镇养老逾五年&#xff0c;竟然不知崇州拥有如此之多的青山绿水&#xff0c;集生态、宜居、智慧、文化、旅游丰富资源于一…

python if语句如何结束

python if语句如何结束&#xff1f;下面给大家介绍两种终止方法&#xff1a; break 用于提前终止循环&#xff1b; num 1 while num < 100:if num > 10:breakprint(num)num 2 print("结束") 结果如下&#xff1a; 1 3 5 7 9 结束 continue 用于跳出当前循…

为 Oh My Zsh 安装 Powerlevel10k 主题

继上一章 安装Zsh 与 oh my zsh 打开终端&#xff0c;运行以下命令&#xff0c;从 GitHub 上克隆 Powerlevel10k 代码库&#xff0c;并将文件放到 Oh My Zsh 的配置文件夹中 git clone https://github.com/romkatv/powerlevel10k.git $ZSH_CUSTOM/themes/powerlevel10k 用文本…

海外短剧平台部署与快速搭建实战指南

目录 一、海外短剧系统是什么 二、搭教程 技术选型 开发前端和后端 三、部分代码展示 随着网络覆盖的广泛扩展与全球化趋势的日益加深&#xff0c;构建面向海外的短视频剧集平台已演变为企业进军国际舞台、拓宽市场边界的关键策略。海外短剧系统不仅承载着将精心制作的短剧…

moment.js时间格式化插件使用

moment.js插件常用api备忘 moment.js插件功能远不不仅仅是在格式化日期上&#xff0c;还是有很多很好用奇淫技巧&#xff0c;使用起来也是更加方便&#xff0c;主要在vue项目中使用偏多&#xff0c;&#xff0c;但是有时候也不是总使用&#xff0c;将一些项目中可能会用&#x…

国内民营企业「数字化转型」典型案例

一、企业简介 三一集团成立于1989年&#xff0c;现有3家上市公司&#xff08;三一重工、三一国际、三一重能&#xff09;&#xff0c;公司总资产超2000亿元&#xff0c;在国内12个省市设有生产基地&#xff0c;在海外建有印度、美国、德国、巴西四大研发制造基地&#xff0c;业…

【国产化信创平台】麒麟银河V10系统虚拟机创建

目录 一、麒麟V10系统镜像下载 二、虚拟机创建流程 三、麒麟银河系统安装流程 一、麒麟V10系统镜像下载 https://www.kylinos.cn/# 官方访问还是会有问题&#xff0c;如果有需要麒麟银河Kylin系统V10的镜像文件&#xff0c;可以留下邮箱或者私信博主获取。 二、虚拟机创…