C语言---冒泡排序和快速排序

news2024/9/20 7:52:01

文章目录

  • 前言
  • 一、冒泡排序
    • 1.简介
    • 2.算法思路
    • 3.代码实现
  • 二、快速排序
    • 1.简介
    • 2.算法思路
      • 2.1左右指针法
      • 2.2挖坑法
      • 2.3前后指针法
  • 总结


前言

交换排序有冒泡排序快速排序这两种,
基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列前部移动。


一、冒泡排序

1.简介

冒泡排序是一种计算机科学领域的较简单的排序算法。
基本原理:对元素个数为 N 的待排序序列进行排序时,共进行N-1次循环。在第 k 次循环中,对从第1到第N-k个元素从前往后进行比较,每次比较相邻的两个元素,若前一个元素大于后一个元素,则两者互换位置,否则保持位置不变。
时间复杂度:O(n²) 空间复杂度:O(1) 稳定性:稳定

2.算法思路

动态显示图,充分理解冒泡排序
在这里插入图片描述
从图中我们可以看见

1.每一次比较都是相邻的两个数,例如图中有15个元素,则比较的下标值是[0,1],[1,2],[2,3],[3,4],[4,5],[5,6],[6,7],[7,8],[8,9],…,[13,14]。共14趟。
2. **第一趟:**先从下标值0开始,每一次比较都是和后一个元素,如果后一个元素大于前一个元素,则进行交换,然后再进行后一个元素的比较,直到与末尾元素比较。此时末尾元素是最大值。
3. 第二趟开始,不需要比较末尾元素了,因为第一趟就是将最大值放在末尾。也就是说需要比较剩余的14个元素,重复步骤二,找到第二大的元素。
4. 依次下去,将大的元素排在后面,就形成了从小到大的有序序列

3.代码实现

#include <stdio.h>
void Swap(int* px, int* py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}
void BubbleSort(int* a, int n)
{
	int end = n;
	while (end > 0)
	{
		int exchange = 0;
		for (int i = 1; i < end; ++i)
		{
			//当下一个元素大于当前元素时,进行交换
			if (a[i - 1] > a[i])
			{
				exchange = 1;
				Swap(&a[i - 1], &a[i]);
			}
		}
		--end;
		//当元素未进行交换,则本就是有序,直接结束循环
		if (exchange == 0)
		{
			break;
		}
	}
}
int main()
{
		int a[] = { 26, 2, 5, 3, 9, 4, 8, 5, 1, 7 };
		BubbleSort(a, 10);
		for (int i = 0; i < 10; i++)
		{
			printf("%d ", a[i]);
		}
}

注:如果序列是有序的,可以局部变量来判断,可以减少运行时间,提高效率。
运行结果:
在这里插入图片描述


二、快速排序

1.简介

快速排序是Hoare于1962年提出的一种二又树结构的交换排序方法
基本思想: 任取待排序元素序列中为某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
时间复杂度:O(N*logN) 空间复杂度:O(logN) 稳定性:不稳定

2.算法思路

快速排序一般有三种方法:左右指针法、挖坑法、前后指针法。而这三种都需要利用到递归方法

2.1左右指针法

思路:

  1. 在待排序序列中,取一个元素为key,key为初始元素或者末尾元素。
  2. 此时取初始元素为key,右边开始遍历,当遍历到比key值小的元素时,停止;开始左边遍历,当左边遍历到比key元素小的时候,进行左右交换
  3. 然后重复操作,直到两边指针相遇。此时左指针的值和key进行交换。然后再按照以上方法进行递归实现【left,keyi】和【keyi+1,right】,keyi是key的下标值

注:key选初始元素,右边先开始遍历;选末尾元素,则左边先开始遍历
动态展示图
在这里插入图片描述
图片详解图:
在这里插入图片描述
绿色交换,是指左指针和右指针相遇之后和key交换;
红色(黄色)交换,是指左右指针的值交换。
从图我们可以看出来,每一次排序,都是将待排序分成2部分,一部分是比key小的,一部分是比key大的,再通过递归的方法,逐次排序。
代码实现

void swap(int* w, int* t)
{
	int tem = *w;
	*w = *t;
	*t = tem;
}
int Partion1(int* p, int left, int right)
{
	//keyi值可进行改进,在文章结尾
	int keyi = left;
	while (left < right)
	{
		while (left < right && p[right] >= p[keyi])
		{
			right--;
		}
		while (left < right && p[left] <= p[keyi])
		{
			left++;
		}

		swap(&p[left], &p[right]);
	}
	swap(&p[left], &p[keyi]);
	return left;
}
void Text1(int *p,int left,int right)
{
	if (left >= right)
		return;
	int key=Partion1(p, left, right);
	//递归方法
	Text1(p, left, key - 1);
	Text1(p, key + 1,right);

}
int main()
{
	int arr[10]={6,1,2,7,9,3,5,10,8};
	Text1(arr,0,9);	
	for(int i=0;i<10;i++)
	{
	printf("%d ",arr[i]);
	}
}
	

2.2挖坑法

思路:

  1. 先将最左边的值保存下来,作为key值。
  2. 将最左边的值抽出来了,然后右边遍历,遍历到比key小的值,填入坑中。相当于把数移到了坑里,而自身留下了坑位,然后左遍历,遍历到比key大的值,再移到右边的坑里。
  3. 重复上述操作,直到左右指针相遇,再把key的值填入相遇的位置。

注:思路和左右指针法是相似的。key选初始元素,右边先开始遍历;选末尾元素,则左边先开始遍历
在这里插入图片描述
代码实现

void swap(int* w, int* t)
{
	int tem = *w;
	*w = *t;
	*t = tem;
}
int Partion2(int* p, int left, int right)
{
	//记录keyi的值。keyi值可进行改进,在文章结尾
	int keyi = p[left];
	int priot = left;
	while (left < right)
	{
		while (left < right && p[right] >= keyi)
		{
			right--;
		}
		p[priot] = p[right];
		priot = right;
		while (left < right && p[left] <= keyi)
		{
			left++;
		}
		p[priot] = p[left];
		priot = left;
	}
	swap(&p[left], &keyi);
	return left;
}
void Text1(int *p,int left,int right)
{
	if (left >= right)
		return;
	int key=Partion2(p, left, right);
	//递归方法
	Text1(p, left, key - 1);
	Text1(p, key + 1,right);

}
int main()
{
	int arr[10]={6,1,2,7,9,3,5,10,8};
	Text1(arr,0,9);	
	for(int i=0;i<10;i++)
	{
	printf("%d ",arr[i]);
	}
}

2.3前后指针法

思路:

1、选出一个key,一般是最左边或是最右边的。

2、起始时,prev指针指向序列开头,cur指针指向prev+1。

3、若cur指向的内容小于key,则prev先向后移动一位,然后交换prev和cur指针指向的内容,然后cur指针++;若cur指向的内容大于key,则cur指针直接++。如此进行下去,直到cur到达end位置,此时将key和++prev指针指向的内容交换即可。

经过一次单趟排序,最终也能使得key左边的数据全部都小于key,key右边的数据全部都大于key。

然后也还是将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作

简单而言就是:cur找到比key小的就停下来,然后++prev,然后cur的值和prev的值交换,cur就再次继续向前移动,直到cur到最右边。
图片展示:
在这里插入图片描述
注:图中是两种前后指针的示意图,如果右边做key,cur指向下标为1,prev则是在后面;如果左边做key,cur则指向下标为2的值,prev也是在后面。

代码展示

void swap(int* w, int* t)
{
	int tem = *w;
	*w = *t;
	*t = tem;
}
int Partion3(int* p, int left, int right)
{
	int prev = left;
	int cur = left + 1;
	int keyi = left;
	while (cur <= right)
	{
		while (cur<=right&&a[cur] >= a[keyi])
		{
			cur++;
		}
		if (cur <= right)
		{
			swap(&a[cur], &a[++prev]);
			cur++;
		}
	}
	swap(&a[prev], &a[keyi]);
	return prev;
}
void Text1(int *p,int left,int right)
{
	if (left >= right)
		return;
	int key=Partion3(p, left, right);
	//递归方法
	Text1(p, left, key - 1);
	Text1(p, key + 1,right);

}
int main()
{
	int arr[10]={6,1,2,7,9,3,5,10,8};
	Text1(arr,0,9);	
	for(int i=0;i<10;i++)
	{
	printf("%d ",arr[i]);
	}
}

改进:在左右指针和挖坑法里,有时候数组的第一个元素或者最后一个元素是序列里最小的,就面对了最坏的情况,如此效率就会下降,为了提高效率,我们将key为三数取中,即第一个元素,中间元素和最后一个元素里取值中的元素。先找到中间元素,然后与key进行互换。
方法如下:

int midnum(int* a, int left, int right)
{
	int mid = left + ((right - left) >> 1);
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] < a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
}
//找到中间值的下标,赋值给min,然后与left的值进行交换
int min = midnum(a, left, right);
	swap(&a[min], &a[left]);

总结

插入排序虽然有三种方式,但都是大同小异,可以通过画图来消化。
文章的动态图全是小小作者我在网上寻找到的,认为最为合适并且容易理解的图。
而静态图则是小小作者我自己画的,可能并不完美,但是认真看,自己画一下,我相信你们一定可以明白的。

排序难以理解,千位要画图理解,要动手处理,不能只看,只敲代码,一定要要画图

                                                                                                  ---------来自菜鸟TQ02的提示

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

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

相关文章

多目标跟踪:文献综述

文章目录摘要1、简介1.1、与其他相关综述的区别1.2、贡献1.3、综述的结构1.4. 外延2、 MOT问题2.1、问题公式化2.2、MOT的分类2.2.1、初始化方法2.2.2、处理方式2.2.3、输出类型2.2.4. 讨论3、MOT的组成3.1、外观模型3.1.1、视觉表征3.1.2. 统计测量3.2、运动模型3.2.1. 线性运…

安装httprunner manager遇到的坑

安装httprunner manager参考博客&#xff1a;https://www.cnblogs.com/MrqiuS/p/12944481.html安装mysqlclient报错Collecting mysqlclient1.3.12Downloading https://mirrors.aliyun.com/pypi/packages/6f/86/bad31f1c1bb0cc99e88ca2adb7cb5c71f7a6540c1bb001480513de76a931/m…

maven利用springboot的配置文件进行多个环境的打包

在Spring Boot中多环境配置文件名需要满足application-{profiles.active}.properties的格式&#xff0c;其中{profiles.active}对应你的环境标识&#xff0c;可以随意命名&#xff0c;但要与pom文件中环境标识一样。至于哪个具体的配置文件会被加载&#xff0c;需要在applicati…

Docker系列 01

前言 学完这篇文章&#xff0c;你会知道Docker是什么&#xff1f;解决了什么问题以及如何安装Docker。在后续的系列文章中&#xff0c;我们会陆续介绍Docker的常用命令、镜像原理、Dockerfile以及网络管理等&#xff0c;让你对Docker有较为系统的认知&#xff0c;持续关注吧。 …

多继承的运用

//在封装一个领导类&#xff08;Leader&#xff09;:包括受保护成员&#xff1a;岗位、工资&#xff0c;完成其相关函数及show//由以上两个类共同把派生出学生干部类&#xff1a;引入私有成员&#xff1a;管辖人数&#xff0c;完成其相关函数以及show函数//在主程序中&#xff…

一刷代码随想录——动态规划

1.理论基础如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。所以动态规划中每一个状态一定是由上一个状态推导出来的&#xff0c;这一点就区分于贪心&#xff0c;贪心没有状态推导&#xff0c;而是从局部直接选最优的。对于动态规划问题&#xff0c;我将拆解…

24考研——高等数学的基础概念定理(一)——第一章|函数、极限、连续

今天考试&#xff0c;考定理&#xff0c;我是真没想到这些定理&#xff0c;脑袋瓜子嗡嗡的&#xff0c;害&#xff0c;废话少说&#xff0c;直接开干 文章目录一、基础概念定理1.1 收敛数列的性质&#xff08;唯一性、有界性、保号性、数列与子列关系&#xff09;1.2 函数极限的…

一款语文老师写的word办公神级插件

没错&#xff0c;正如题所说&#xff0c;这是一款语文老师开发的word办公神级插件----不坑盒子&#xff0c;小编体验了一下还真是太强大了&#xff01;双击exe文件&#xff0c;选择任意安装目录即可开始安装如果你的电脑缺少运行库&#xff0c;软件会自动帮你下载安装好安装完成…

QT模块化编程 pro pri 子工程 使用

如果工程过大时&#xff0c;就需要分子模块&#xff0c;Qt 也给我们提供了一种方式 Pri&#xff1b; 这里我们来看看Pri在项目中的使用&#xff0c;项目使用实际上也是很有主要可以模块清晰化&#xff0c;能提高程序的可扩展性。 网上这种写的很多&#xff0c;但少有写明白的&…

EGFR靶点药物研发进展-销售数据-上市药品前景分析

根据世界卫生组织的最新报告&#xff0c;与世界上其他癌症相比&#xff0c;肺癌导致的死亡率最高。非小细胞肺癌 (NSCLC) 约占肺癌病例总数的 85%。大量的风险因素归因于肺癌的发展&#xff0c;表皮生长因子受体 (EGFR) 是最常见的突变驱动基因之一&#xff0c;通过调节 PI3K/A…

Sentinel之授权规则与规则持久化

一、上集回顾上级文章地址&#xff1a;【SpringCloud】Sentinel 之隔离与降级_面向架构编程的博客-CSDN博客我们先来回顾一下Sentinel控制台上面的功能&#xff1a;所以&#xff0c;本章节就来研究 "授权规则"这一功能模块。二、授权规则授权规则可以对请求方来源做判…

XV6实验(2020)

XV6实验记录(2020) 环境搭建 参考连接 Lab guidance (mit.edu) 6.S081 / Fall 2020 (mit.edu) xv6 book中文版 Lab1:Xv6 and Unix utilities 实现几个unix实用工具&#xff0c;熟悉xv6的开发环境以及系统调用 Boot xv6 就是准备环境&#xff0c;克隆仓库&#xff0c;编…

代码随想录算法训练营第五十三天|● 1143.最长公共子序列 ● 1035.不相交的线 ● 53. 最大子序和 动态规划

一、1143.最长公共子序列 题目&#xff1a; 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长公共子序列的长度。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符&#xff08;也可以不…

Android 自定义控件

文章目录Canvas 画布类 画布背景 点 线 矩形 椭圆 圆 弧形 路径 字符 对画布裁剪及变形Paint 画笔类 常用方法 图形线条相关 字符相关 Path设置样式如果是一个自定义控件&#xff0c;则需要派生自 Vie…

【Vue】模板语法——内置指令

指令&#xff08;Directives&#xff09;是 vue 为开发者提供的模板语法&#xff0c;用于辅助开发者渲染页面的基本结构。vue 中的指令按照不同的用途可以分为如下几大类&#xff1a;① 内容渲染指令&#xff1a;v-text、v-html② 属性绑定指令&#xff1a;v-bind③ 事件绑定指…

<Java EE 进阶> 3.Spring简单的读和取

目录 1.存储Bean对象 &#xff08;1&#xff09;准备工作&#xff1a;配置扫描路径 &#xff08;2&#xff09;添加注解存储Bean对象 ① 类注解 ② 方法注解Bean 在String中更简单的存储和读取对象的核心是使用注解 1.存储Bean对象 &#xff08;1&#xff09;准备工作&am…

Linux内核的安装与加载

目录 一、tftp加载Linux内核和roootfs 二、 EMMC加载Linux内核和rootfs 三、tftp加载Linux内核nfs挂在根文件系统 四、EMMC加载uboot 一、tftp加载Linux内核和roootfs 这个就是Linux内核&#xff0c;它很轻量级只有2.949MB所以在嵌入式领域很受欢迎。 上面那个就是设备树文…

初识 Python 科学计算库之 NumPy(创建多维数组对象)

文章目录参考描述NumPy特点获取导入多维数组对象np.array()np.asarray()范围随机概览np.random.randn()np.random.normal()np.random.choice()np.random.random()np.random.randint()np.random.shuffle()np.random.seed()数列等差数列等比数列填充np.zeros()np.zeros_like()np.…

Spotify Q4用户增长再超预期,但为何还是赚不到钱?

2022年&#xff0c;美联储接连7次暴力加息&#xff0c;科技行业整体低迷&#xff0c;从Meta、Google再到亚马逊&#xff0c;大型科技公司接连宣告裁员过冬。 寒气已经传递到了更广阔的地方。1月下旬&#xff0c;瑞典音乐流媒体巨头Spotify宣布将裁员6%。 音乐流媒体的生意变得…

Python自动化测试实战篇(1)读取xlsx中账户密码,unittest框架实现通过requests接口post登录网站请求,JSON判断登录是否成功

Python接口项目实战篇&#xff08;1&#xff09;读取xlsx中账户密码&#xff0c;unittest框架实现通过requests接口post登录网站请求&#xff0c;JSON判断登录是否成功实现功能描述1.首先获取到接口谷歌浏览器中获取接口信息fiddler里面抓取接口信息2.创建一个xlsx文档3.导入我…