选择排序,冒泡排序,插入排序,快速排序及其优化

news2024/11/18 5:17:56

目录

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 代码实现

3.4 优化

4. 快速排序 

4.1 原理

4.2 具体步骤

4.3 代码实现 

4.4 优化 


为了讲解方便,以下排完序后,统一为升序

1 选择排序

1.1 原理

核心思想是通过不断地选择未排序序列中的最小元素,然后将其放到已排序序列的末尾(或未排序列的起始位置)

 

1.2 具体步骤 

1. 初始状态:所有元素初始都为未排序状态

2 在未排序元素中,找到最小的那个元素的下标

3 与未排序的第一个元素(已排序的末尾元素)交换位置

4 循环 2 ~ 3,直到所有元素都变为已排了的元素

1.3 代码实现

代码实现的关键点:找下标,换位置,以及循环条件。同时也是容易出错的点。

#include<stdio.h>

void sort(int* p, int n)
{
	for (int i = 0; i < n - 1; i++) // < n 也可以,只是无意义的重复,效率更低
	{
		int min = i;   // 找出的最小值,最后要放的位置的下标
		int j = i;
		for (j = i + 1; j < n; j++)  // 可以=i,只是=i,无意义。
		{
			if (p[min] > p[j])
				min = j;	
			//  for循环结束后,j会++  
			if (n - 1 == j)
				break;
		}
		// 交换数据
		int temp = p[i];
		p[i] = p[min];
		p[min] = temp;
	}
}

int main()
{
	int arr[] = { 9,6,8,-1,0,5,2,8 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	sort(arr, sz);  // 选择排序

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

1.4 优化

 原来一趟只找出最小值,现在一趟既找出最小值,也找出最大值,循环的次数就减半了。

#include<stdio.h>

void swap(int* a, int* b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}

void sort(int* p, int n)
{
	// 循环趟数减半,可以了就要停止,不然就会继续换,反而无序
	for (int i = 0; i <= n / 2 + 1; i++) 
	{
		int min = i;   // 找出的最小值的下标
		int max = n -i - 1; // 找出的最大值的下标
		int j = i;
		for (j = i; j < n - i; j++) 
		{
			if (p[min] > p[j])
				min = j;
			if (p[max] < p[j])
				max = j;
			if (n - 1 - i == j)
				break;
		}
	
		// 交换数据
	   // 当最小值与最大值恰好位置相反,换两次=没换
		if (!(p[min] == p[n - 1 - i] || p[i] == p[max]))
		{
			swap(&p[i], &p[min]);
			swap(&p[n - 1 - i], &p[max]);
		}
		else
		{
			swap(&p[i], &p[n - 1 - i]);
		}
	}
}

int main()
{
	int arr[] = { 9,6,8,-1,0,5,2,8 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	sort(arr, sz);  // 选择排序优化

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

2 冒泡排序

2.1 原理

核心思想是通过重复交换相邻元素来实现排序。

类比选择排序,相当于从右往左开始排,每次在未排序中找出最大值,放在已排序的前一个位置。

 

2.2 具体步骤

1. 从左往右相邻元素比较,让大的数不断右移

2. 循环1,直至每个已排序的元素 = 所有元素的个数

2.3 代码实现

关键点:循环条件的控制,以及交换(大的靠右)

#include<stdio.h>

void sort(int arr[], size_t sz)
{
	for (int i = 0; i < sz - 1; i++)
	{
		// 在这里j可以 < sz - 1;
		// 在本段代码中交换位置是有条件的
		// < sz - 1;进去了也不会执行
		// 选择排序从哪开始到哪结束就必须是那样
		// 不可能在再在排了序中挑最大最小值
		for (int j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}

int main()
{
	int arr[] = { 9,6,8,-1,0,5,2,8 };
	size_t sz = sizeof(arr) / sizeof(arr[0]);
	
	sort(arr, sz);  // 冒泡排序
	
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	
	return 0;
}

2.4 优化

如果我们一开始拿到的数组就是有序的话,我们还是不得不执行那循环套循环,效率就很低。我们可以先假设已达到了有序状态,如果交换了,就通过修改flag的值来办,这样就可以提前跳出循环了。

#include<stdio.h>

void sort(int arr[], size_t sz)
{
	for (int i = 0; i < sz - 1; i++)
	{
		// 假设已经到达了有序状态
		int flag = 1;
		for (int j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
				// 交换了,说明无序
				flag = 0;
			}
		}
		if (flag)
		{
			break;
		}
	}
}

int main()
{
	int arr[] = { 9,6,8,-1,0,5,2,8 };
	size_t sz = sizeof(arr) / sizeof(arr[0]);

	sort(arr, sz);  // 冒泡排序优化

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

3 插入排序

3.1 原理

核心思想是构建有序序列将未排序的元素逐个插入到已排序的部分。

左边是已排序的,右边是未排序的。未排序的从左到右第一个,放到已排序列中开始交换,大的就右移,移到不能再移。

 

3.2 具体步骤 

1. 初始化:左边第一个是已排序的,右边都是未排序的。

2 交换位置:未排序的第一个进入排序中,比较大小,大的右移,移到不能再移

3 循环 1 ~ 2,直到遍历数组中的所有元素

3.3 代码实现

关键点: 交换位置的意识,递推的意识

#include<stdio.h>

void sort(int arr[], size_t sz)
{
	// 外层每一次循环都会让已排序的元素+1
	for (int i = 1; i < sz; i++)
	{
		int j = i;
		while (j >= 1 && arr[j] < arr[j - 1])
		{
			// 交换位置
			arr[j - 1] = arr[j] ^ arr[j - 1];
			arr[j] = arr[j] ^ arr[j - 1];
			arr[j - 1] = arr[j] ^ arr[j - 1];
			j--;
		}

	}
}

int main()
{
	int arr[] = { 9,6,8,-1,0,5,2,8 };
	size_t sz = sizeof(arr) / sizeof(arr[0]);

	sort(arr, sz);  // 插入排序

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

3.4 优化

所谓优化算法就更接近于通俗意义上的插入,找到该插入的的地方,再进行插入。即对插入的位置实行二分查找。

#include<stdio.h>

// 返回值是该数插入进去后的下标
int find(int arr[], int sz)
{
	//    1 2 4 5 7 8 9   6
	int left = 0;
	int right = sz - 1;
	while (left < right)
	{
		// 防止陷入死循环
		if (left == right)
			break;
		int mid = right + (left - right) / 2;
		if (arr[mid] > arr[sz])
		{
			right = mid - 1;
		}
		else if (arr[mid] < arr[sz])
		{
			left = mid + 1;
		}
		else
		{
			right = left;
			break;
		}
	}
	if (arr[sz] < arr[right])
	{
		return right;
	}
	else
	{
		return right + 1;
	}
}

void sort(int arr[], size_t sz)
{
	for (int i = 1; i < sz; i++)
	{
		// 本质:二分查找 + 交换
		int temp = arr[i]; // 将要排序的数暂时储存起来
		
		// 二分查找找到应该插入的下标
		int final_local = find(arr, i);
		// 该右移的右移
		for (int j = i - 1; j >= final_local; j--)
		{
			arr[j + 1] = arr[j];
		}
		arr[final_local] = temp;
	}
}

int main()
{
	int arr[] = { 9,6,8,-1,0,5,2,8 };
	size_t sz = sizeof(arr) / sizeof(arr[0]);

	sort(arr, sz);  // 插入排序优化

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

4. 快速排序 

4.1 原理

核心思想是分治法一分为二,左边比某个基准数小,右边比某个基准数大,左边右边又一分为二,直至不可再分

 

4.2 具体步骤

  1. 从数列中随便挑一个数作为基准数(我选的是最后一个数);

  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。

  3. 递归地把小于基准值元素的子数列和大于基准值元素的子数列排序;

4.3 代码实现 

关键点:递归的思想,函数要被调用,要写得一般一些

#include<stdio.h>
/*
函数功能:将最后一个元素作为基准数
         小于它的放左边,大于它的放右边
返回值:基准数最后的位置
*/
int partition(int arr[], int start, int end)
{
	// 遍历基准元素前的所有元素
	for (int i = end - 1; i >= start; i--)
	{
		if (arr[i] > arr[end])
		{
			int temp = arr[i];
			arr[i] = arr[end - 1];
			arr[end - 1] = arr[end];
			arr[end] = temp;
			end -= 1;
		}
	}
	return end;
	//  2 1 3 2
}

void QuickSort(int arr[], int start, int end)
{
	if (start < end)
	{
		// 函数最后返回的是排过后基准元素的位置
		int pivot = partition(arr, start, end);
		// 递推式的一分为二
		QuickSort(arr, start, pivot - 1);
		QuickSort(arr, pivot + 1, end);
	}
}

int main()
{
	int arr[] = { 9,6,8,-1,0,5,2,8 };
	size_t sz = sizeof(arr) / sizeof(arr[0]);

	// 以后还要调用,需写得一般一些
	QuickSort(arr, 0, sz - 1);  // 快速排序

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

4.4 优化 

快速排序的效率在于其平均时间复杂度为O(nlogn),这使其成为实际应用中非常受欢迎的一种排序算法。然而,在最坏的情况下,其时间复杂度会退化到O(n^2),这通常发生在每次选择的基准都是最大或最小元素时。为了避免这种情况,可以采用随机选择基准或者三数取中等策略来优化快速排序的性能。下面演示随机选择的优化

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
/*
函数功能:将最后一个元素作为基准数
         小于它的放左边,大于它的放右边
返回值:基准数最后的位置
*/
int partition(int arr[], int start, int end)
{
	int end_temp = end;
	end = rand() % (end - start) + start + 1;
	// 把左边大的甩到右边
	for (int i = end - 1; i >= start; i--)
	{
		if (arr[i] > arr[end])
		{
			int temp = arr[i];
			arr[i] = arr[end - 1];
			arr[end - 1] = arr[end];
			arr[end] = temp;
			end -= 1;
		}
	}
	// 把右边小的甩到左边
	for (int i = end + 1; i <= end_temp; i++)
	{
		if (arr[i] < arr[end])
		{
			int temp = arr[i];
			arr[i] = arr[end + 1];
			arr[end + 1] = arr[end];
			arr[end] = temp;
			end += 1;
		}
	}
	return end;
	//  2 1 3 2
}

void QuickSort(int arr[], int start, int end)
{
	if (start < end)
	{
		// 函数最后返回的是排过后基准元素的位置
		int pivot = partition(arr, start, end);
		// 递推式的一分为二
		QuickSort(arr, start, pivot - 1);
		QuickSort(arr, pivot + 1, end);
	}
}

int main()
{
	srand((unsigned int)time(NULL));
	int arr[] = { 9,6,8,-1,0,5,2,8 };
	size_t sz = sizeof(arr) / sizeof(arr[0]);

	// 以后还要调用,需写得一般一些
	QuickSort(arr, 0, sz - 1);  // 快速排序

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

感谢观看!!!

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

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

相关文章

如何优化一个看似正常的数据库

通常DBA是不会太了解业务逻辑的&#xff0c;遇到系统中劣质的sql 一般也是以通过添加索引的方式来优化&#xff0c;但是并不是所有的sql都能通过添加索引来优化 这就需要重sql的本身来做分析&#xff0c;另外还要了解什么样的语句会不走索引&#xff01;本文通过几个简单的例子…

RK3568 android11 调试陀螺仪模块 MPU-6500

一&#xff0c;MPU6500功能介绍 1.简介 MPU6500是一款由TDK生产的运动/惯性传感器&#xff0c;属于惯性测量设备&#xff08;IMU&#xff09;的一种。MPU6500集成了3轴加速度计、3轴陀螺仪和一个板载数字运动处理器&#xff08;DMP&#xff09;&#xff0c;能够提供6轴的运动…

计算机网络——IPV4数字报

1. IPv4数据报的结构 本结构遵循的是RFC 791规范&#xff0c;介绍了一个IPv4数据包头部的不同字段。 1.1 IPv4头部 a. 版本&#xff08;Version&#xff09;&#xff1a;指明了IP协议的版本&#xff0c;IPv4表示为4。 b. 头部长度&#xff08;IHL, Internet Header Length&…

web组态软件

1、强大的画面显示web组态功能 2、良好的开放性。 开放性是指组态软件能与多种通信协议互联&#xff0c;支持多种硬件设备&#xff0c;向上能与管理层通信&#xff0c;实现上位机和下位机的双向通信。 3、丰富的功能模块。 web组态提供丰富的控制功能库&#xff0c;满足用户的测…

回归预测 | Matlab实现OOA-HKELM鱼鹰算法优化混合核极限学习机多变量回归预测

回归预测 | Matlab实现OOA-HKELM鱼鹰算法优化混合核极限学习机多变量回归预测 目录 回归预测 | Matlab实现OOA-HKELM鱼鹰算法优化混合核极限学习机多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现OOA-HKELM鱼鹰算法优化混合核极限学习机多变量…

《互联网的世界》第二讲-最短路径优先

昨天讲 dns 时讲过&#xff0c;“你问一个当地人最近的厕所在哪&#xff0c;路人给你一个地址…”&#xff0c;可是只有地址还不够&#xff0c;如何到达那里呢&#xff1f;这是本节的内容。 自然的方式是&#xff0c;一边走一边问&#xff0c;根据路人的指示继续一边走一边问…

pikachu之xss获取键盘记录

前备知识 跨域 跨域&#xff08;Cross-Origin&#xff09;是指在互联网中&#xff0c;浏览器为了保护用户信息安全而实施的一种安全策略——同源策略&#xff08;Same-Origin Policy&#xff09;&#xff0c;即浏览器禁止一个域上的文档或者脚本&#xff08;如通过JavaScript发…

单片机复位按键电路、唤醒按键电路

目录 单片机复位按键 外部手动复位 单片机复位按键电路 复位按键电路1 复位按键电路2 单片机唤醒按键 单片机唤醒按键电路 单片机复位按键 单片机复位&#xff1a;简单来说&#xff0c;复位引脚就是有复位信号&#xff0c;就是从头开始执行程序 本质&#xff1a;就是靠…

Linux内核适配 (一)

我们的产品包含多个内核驱动模块,随着Linux内核的不断演进,既有的驱动代码可能因为使用了一些被新版本内核所废弃的函数或者数据结构,导致不能编译通过,或者运行时出错。这时,我们就需要修改我们的驱动代码,以便其能在新版本的内核上正常工作,这个过程通常被称为「适配」…

【机器学习】线性回归模型(Linear Regression)

&#x1f338;博主主页&#xff1a;釉色清风&#x1f338;文章专栏&#xff1a;机器学习&#x1f338;今日语录:温柔的一半是知识&#xff0c;没有知识的涵养撑不起你想要的风骨。 ☘️0文章预览 本系列文章主要是根据吴恩达老师的机器学习课程以及自己的理解整合而成&#xf…

【GO开发工程师】grpc进阶#golang

【GO开发工程师】grpc进阶#golang 推荐个人主页&#xff1a;席万里的个人空间 文章目录 【GO开发工程师】grpc进阶#golang1、protobuf2、grpc2.1、gRPC 的 Metadata机制2.2、grpc拦截器 1、protobuf syntax "proto3"; // 指定使用的 protobuf 版本为 proto3 import…

react-JSX基本使用

1.目标 能够知道什么是JSX 能够使用JSX创建React元素 能够在JSX中使用JS表达式 能够使用JSX的条件渲染和列表渲染 能够给JSX添加样式 2.目录 JSX的基本使用 JSX中使用JS表达式 JSX的条件渲染 JSX的列表渲染 JSX的样式处理 3.JSX的基本使用 3.1 createElement()的问题 A. …

基于Springboot的计算机知识竞赛网站(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的计算机知识竞赛网站&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结…

Python学习 day05(异常、模块导入、包)

异常 为什么要捕获异常 当程序遇到了BUG&#xff0c;如果不对BUG进行手动捕获&#xff0c;那么整个程序就会因为一个BUG而停止运行&#xff0c;这在有些情况下是会造成很大的损失&#xff0c;但是如果我们进行了手动捕获&#xff0c;那么整个程序会继续运行捕获异常的作用在于&…

ubantu与windows文件传输(filezilla)

ubantu与windows文件传输&#xff08;filezilla&#xff09; windowsubantu20.04Fliezilla windows 到官网下载filezilla&#xff1a;https://www.filezilla.cn/&#xff0c;并安装。 ubantu20.04 1、安装vim sudo apt-get install vim2、安装FTP服务 sudo apt-get instal…

【机器人最短路径规划问题(栅格地图)】基于遗传算法求解

基于遗传算法求解机器人最短路径规划问题&#xff08;栅格地图&#xff09;的仿真结果 仿真结果&#xff1a; 路径长度的变化曲线&#xff1a; 遗传算法优化后的机器人避障路径&#xff1a;

Python入门学习:if语句与条件控制--and、or、in、not in详解与实践

Python入门学习&#xff1a;if语句与条件控制–and、or、in、not in详解与实践 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1…

[AIGC] JDK17中的Record类介绍

文章目录 什么是Record类Record类的特点Record类实践 我们都知道&#xff0c;从Java 14开始, JEP 359 推出了一个新的类型声明Record&#xff0c;Record 类型用来模拟不可变的数据结构&#xff0c;它能产生一个类包含一定数量的只读字段。 什么是Record类 在JDK14中引入了一…

jetson nano——编译安装PySide2

目录 1.打开我提供的文件or官网自己下载&#xff08;需对应PyQt5的版本&#xff09;2.解压文件3.进入目录4.安装clang5. 编译安装6.报错: error: ‘NPY_ARRAY_UPDATEIFCOPY’ was not declared in this scope7.又报错&#xff1a;error: ‘NPY_ARRAY_UPDATEIFCOPY’ was not de…

Yapi部署

【GO开发工程师】Yapi部署 推荐个人主页&#xff1a;席万里的个人空间 文章目录 【GO开发工程师】Yapi部署1、Yapi部署 1、Yapi部署 初始化yapi&#xff1a; git clone https://github.com/Ryan-Miao/docker-yapi.git cd docker-yapi docker-compose upyapi启动失败 1.cd进入…