《数据结构》(六)八大排序(上)

news2024/11/19 16:33:22

生活中大家从小到大处处可见排队,但是排队有哪些快速的方法你了解吗?

在这里插入图片描述


八大排序

  • 排序的基本概念
  • 插入排序
    • 直接插入排序
      • 基本思想
      • 代码
      • 直接插入排序总结
    • 希尔排序
      • 基本思想
      • 代码
      • 希尔排序总结
  • 选择排序
    • 直接选择排序
      • 基本思想:
      • 代码
      • 直接选择排序总结
    • 堆排序
      • 堆的基本概念
      • 向下调整算法
      • 堆排序的基本思想(大堆)
      • 代码
      • 堆排序总结
  • 结语

排序的基本概念

在这里插入图片描述

  • 排序 (Sorting) : 是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列;
  • 稳定性: 假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
  • 内部排序: 指的是待排序记录存放在计算机随机存储器中进行的排序过程;
  • 外部排序: 指的是待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中尚需对外存进行访问的排序过程。

插入排序

直接插入排序

基本思想

  • 直接插入是一种最简单的排序方法,它的基本操作是将一个记录插入到已排好序的有序序列中,从而得到一个新的,记录数增1的有序表;
  • 先将序列中的第1个记录看成是一个有序的子序列,然后从第2个记录起逐个进行插入,直至整个序列变成有序序列为止,整个排序过程进行n-1趟插入;
  • 直接插入排序就像是大家斗地主一样😁😁😁,假定我们手中已经有了[0,end]张有序的牌,这时我们摸了一张牌,将新摸得这张牌插入到我们手中[0,end]里,并使[0,end+1]有序。
    在这里插入图片描述

代码

void InsertSort(int* a, int n)
{
	//[0,end]有序,把 end+1位置的值插入[0,end],让[0,end+1]有序
	for (int i = 0; i < n - 1; ++i)//这里的for循环,就相当于摸牌,n-1就相当于斗地主中,非地主一共摸几张
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (a[end] > tmp)
			{
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}
void TestInsertSort()
{
	int a[] = { 3,5,2,7,8,6,1,9,4,0 };
	InsertSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

直接插入排序总结

  • 元素集合越接近有序,直接插入排序算法的时间效率越高
  • 时间复杂度:O(N^2)
  • 稳定性:稳定

希尔排序

希尔排序是直接插入排序的优化,又被称为“缩小增量排序”,虽然它也是一种属插入排序类的方法,但是时间效率相比直接插入排序有较大改进。😎😎😎


基本思想

大致分为两步:1.预排序让数组接近有序,2.直接插入排序(上面直插总结,元素越接近有序,直插效率越高)。
先选一个整数(gap),把数组中间隔为gap的分为一组,并将每组排好序,再让gap/2,并重复分组和排序,当gap=1时,数组中就排好序了。 其中每组排序用的是直接插入排序
在这里插入图片描述

  • 多组间隔为gap的预排序,gap由大变小;
  • gap越大,元素中最大和最小越快到它相应地位置,但是预排越不接近有序;
  • gap越小,越接近有序;
  • gap等于1时,那就成直接插入排序了。

代码

void ShellSort(int* a, int n)
{
	int gap=n;
	while (gap > 1)
	{
		//gap=gap/3+1;
		gap = gap / 2;//除三或者除二都是可以的,除二的话能保证最后gap一定为1
		// gap>1时都是预排序--目的使其接近有序
		// gap==1时就是直接排序
		
		//gap很大时,下面预排序的时间复杂度是O(N)
		// gap很小时,数组已经很接近有序了,这是差不多是O(N)
		
		//把间隔为gap的多组数据同时排
		for (int i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{

				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -=	gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}
void TestShellSort()
{
	int a[] = { 9,8,7,6,5,4,3,2,1,0 };
	ShellSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

希尔排序总结

  • 希尔排序是对直接插入排序的优化;
  • 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,就成直接插入排序;
  • 稳定性:不稳定;
  • 时间复杂度:挺难算的,前辈求出的是 O(N^1.3——N ^2)。

选择排序

直接选择排序

基本思想:

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,最大的放到序列的最后面,直到全
部待排序的数据元素排完 。
😧😧😧

实现

  • 在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素;
  • 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)
    元素交换;
  • 在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩
    余1个元素。

在这里插入图片描述


代码

void Swap(int*p1,int*p2)//交换
{
	int* tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int min = begin, max = begin;
		for (int i = begin; i <= end; ++i)
		{
			//分别选出数组中最大的数和最小的数,分别赋给min和max;
			if (a[i] < a[min])
			{
				min = i;
			}
			if (a[i] > max)
			{
				max = i;
			}
		}
		Swap(&a[begin], &a[min]);
		// 如果begin跟max重叠,需要修正一下max的位置
		if (begin == max)
		{
			max = min;
		}
		Swap(&a[max], &a[end]);
		++begin;
		--end;
	}
}

void TestSelectSort()
{
	int a[] = { 3, 5, 2, 7, 8, 6, -1, 9, 9, 4, 0 };
	SelectSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

直接选择排序总结

  • 直接选择排序很简单,只看代码就能轻松理解,但是实用性很低,大家了解即可;
  • 时间复杂度:O(N^2);
  • 稳定性:不稳定。

堆排序

堆的基本概念

之前在二叉树中写过链式结构,今天的堆排序顺带着给大家写一下二叉树的顺序结构。

堆的逻辑结构与物理结构

  • 堆的逻辑结构是一颗完全二叉树(想象出来的),堆的物理结构是一个数组。

堆的两大特性

  • 结构性:用数组表示的完全二叉树;
  • 有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)
    1.“最大堆”,也称为“大顶堆”: 最大值,要求:树中所有父亲都大于等于孩子;
    2.“最小堆”,也称为“小顶堆”: 最小值,要求:树中所有父亲都小于等于孩子;
    在这里插入图片描述
    从上图我们发现,小堆它的根一定是最小的,而大堆的根一定是最大的。
    父子间下标的关系
    leftchild是左孩子结点,rightchild是右孩子结点,parent是父亲结点,child是孩子结点
  • leftchild=parent*2+1;
  • rightchild=parent*2+1+1=leftchild+1;
  • parent=(child-1)/ 2。

向下调整算法

建小堆:选出左右孩子中小的哪一个,跟父亲交换,小的往上浮,大的往下沉,如果要建大堆则相反

向下调整算法前提

  • 若想将其调整为小堆,那么根结点的左右子树必须都为小堆。
  • 若想将其调整为大堆,那么根结点的左右子树必须都为大堆。

向下调整算法基本思想(以小堆为例子)

  1. 从根结点处开始,选出左右孩子中值较小的孩子;
  2. 让小的孩子与其父亲进行比较;
  3. 若小的孩子比父亲还小,则该孩子与其父亲的位置进行交换。并将原来小的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止;
  4. 若小的孩子比父亲大,则不需处理了,调整完成,整个树已经是小堆了。

如何选择建大堆还是小堆?

  1. 升序建大堆,因为大堆的根节点是整颗树的最大值;
  2. 降序建小堆,因为小堆的根节点是整颗树的最小值。

堆排序的基本思想(大堆)

因为我们这里用到的是升序,所以就用大堆的方式
先将一个数组构建一个大顶堆,此时,最大值就是根结点,将其与末尾元素进行交换,这时末尾就是最大值,然后再将剩余n-1个元素再次构建一个大顶堆,以此循环,就可以得到一个有序序列。😋😋😋


代码

//交换两个变量--传址调用
void Swap(int*p1,int*p2)
{
	int* tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//向下调整算法--大堆
void AdjustDwon(int* a, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child += 1;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapSort(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDwon(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon(a, end, 0);
		--end;  
	}
}
void TestHeapSort()
{
	int a[] = { 3, 5, 2, 7, 8, 6, 1, 9, 4, 0 };
	HeapSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

堆排序总结

  • 堆排序用堆来选数,效率是很高的
  • 时间复杂度:O(N*logN)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

结语

😊😊😊到了这里大家对于插入排序和选择排序这两类已经基本掌握了,下篇会得大家带来冒泡排序,三种快速排序,计数排序和归并排序。😚😚😚

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

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

相关文章

大数据基础之java常用API一

常用API1. Object类2. String类String案例1. Object类 构造方法空参构造全参构造 Object类: 是所有类的基类,或者说公共父类,每个类都直接或者间接的继承自Object,所以该类中有的方法,其他类中都有 构造方法: public Object(); 所有类的构造方法中都会默认调用super() 会逐级调…

C#里在子窗口与父窗口之间进行数据传送

在C#里经常需要在子窗口与父窗口之间进行数据传送,或者调用,虽然有很多方法可以实现,但是采用委托还是比较简单和直接的方式。 所以这次针对委托来演示一下怎么样实现这种功能。 下面先来创建一个带两窗口的例子,如下图所示: 接着来看一下,创建父窗口的代码: namespace…

【C++笔试强训】第十一天

&#x1f387;C笔试强训 博客主页&#xff1a;一起去看日落吗分享博主的C刷题日常&#xff0c;大家一起学习博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a;夜色难免微凉&#xff0c;前方必有曙光 &#x1f31e;。 &#x1f4a6; &…

vite基础知识-1

为什么选择vite&#xff1f; 讲vite之前&#xff0c;我们先来了解一下webpack的原理。 webpack支持多种模块化&#xff08;浏览器端和服务端都可以运行&#xff09;。比如&#xff1a; // index.js const lodash require("lodash"); // commonjs规范 import React…

win10 docker desktop 报 docker desktop stopped

win10电脑安装doker deskto遇到一些问题解决过程记录 报 docker desktop stopped 没过多会, docker desktop就自动退出了, 要以理解为闪退 网上查了一下原因, 虚拟化可能没设置 进入bios, 发现笔记本电脑没有这个设置 重启电脑后, 弹出消息 WSL 2 installation is incompl…

通道分离与合并、彩色图转换为灰度图、二值化

文章目录图像基础重要的函数图像基本知识图像基础通道分离与合并彩色图转换为灰度图二值化图像的加减乘除图像基础 矩阵分辨率8位整型图像浮点数图像 现在简单介绍下二值化、灰度图以及真彩色和假彩色 图像的二值化&#xff0c;就是将图像上的像素点的灰度值设置为0或255&am…

安装宝塔面板(详细教程)

目录 安装宝塔面板 &#xff08;一&#xff09;前言 &#xff08;二&#xff09;准备工作 1、官网&#xff1a;宝塔面板下载&#xff0c;免费全能的服务器运维软件 &#xff08;三&#xff09;安装宝塔面板 1、进入官网&#xff0c;选择“安装脚本” 2、选择对应版本的安…

C++游戏开发小笔记

1.入门小语法 1.1 命名空间 当想使用库文件的某个函数时&#xff0c;为了防止由于重名而引起的混乱调用&#xff0c;使用命名空间来区分同名函数。 字符串String也是标准命名空间的一个。如果没有using namespace std; 想用string 类型得 std:: string 1.2浮点数的存储…

计算机视觉--flask部署 目标检测算法,并在局域网内远端访问

1.flask框架 Flask是一个轻量级的基于Python的web框架。static 文件夹来保存静态文件&#xff0c;templates 文件夹存放前端页面 安装&#xff1a; pip install Flask框架代码&#xff1a; from flask import * from flask import Flaskapp Flask(__name__) //获取实例app.…

Web监听器:Listener

Listener简介常用监听接口监听在线用户信息的实现Model层Controller层OnlineUserListener的实现View层测试简介 监听器&#xff08;Listener&#xff09;&#xff0c;是一个实现特定接口的普通Java程序&#xff0c;用于监听Web应用中的对象或信息发生改变时&#xff0c;作出相应…

大前端进阶

目录 1.概述和前端工具vscode安装 1.下载安装VScode 2.中文界面配置 3.插件安装 4.设置字体大小 5.开启完整的Emmet语法支持 2.Nodejs 1.Nodejs介绍与安装 2.快速入门-Hello World 3.Node - 实现请求响应 4.Node-操作MYSQL数据库 3.Es6 1.ES6的概述 2.ES6的语法…

Docker实战:使用Dockerfile部署第一个netcore程序

目录 1、创建一个NetCore API项目 2、云服务部署 2.1 首先登录云服务器创建一个测试目录 2.2 新建dockerfile文件用来构建镜像。 2.3 构建镜像 2.4 运行镜像 目录 1、创建一个NetCore API项目 2、云服务部署 2.1 首先登录云服务器创建一个测试目录 2.2 新建dockerfil…

Python3,10h行代码,制作艺术签名,从此走上人生巅峰。

制作艺术签名1、引言2、代码实战2.1 代码示例3、总结1、引言 小鱼&#xff1a;小屌丝&#xff0c;你在画啥呢&#xff1f; 小屌丝&#xff1a;… 我这在练习签名呢。 小鱼&#xff1a;这… 艺术签&#xff1f; 小屌丝&#xff1a;喔&#xff0c;鱼哥&#xff0c;你这真实博才多…

CSDN云IDE怎样克隆代码

文章目录1、怎样从GitHub导入项目&#xff1f;1.1、系统生成SSH-KEY2、克隆代码2.1、克隆代码方式一2.2、克隆代码方式二2.3、克隆代码方式三1、怎样从GitHub导入项目&#xff1f; 1.1、系统生成SSH-KEY 1、打开Terminal 2、查看是否存在现有的ssh密钥 $ ls -al ~/.ssh3、检…

【LeetCode】【二叉树的最近公共祖先】

力扣 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自…

《JavaSE-第十四章》之文件(一)

前言 在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!” 博客主页&#xff1a;KC老衲爱尼姑的博客主页 博主的github&#xff0c;平常所写代码皆在于此 刷题求职神器 共勉&#xff1a;talk is cheap, show me the code 作者是爪哇岛的新手&#xff0c;水…

Go 语言中的并发编程(Let‘s Go 二十八)

Go从底层就支持并发编程&#xff0c;同时实现垃圾回收机制。 先了解并发相关的几个名词概念。 进程&#xff1a;系统进行资源分配和调度的基本单位&#xff0c;使系统结构的基础&#xff0c;它是一个实体。 线程&#xff1a;是程序中一个单一的顺序执行流程&#xff0c;是进程…

python做了个自动关机工具,再也不会耽误我下班啦

上班族经常会遇到这样情况&#xff0c;着急下班结果将关机误点成重启&#xff0c;或者临近下班又通知开会&#xff0c;开完会已经迟了还要去给电脑关机。 【阅读全文】 今天使用PyQt5做了个自动关机的小工具&#xff0c;设置好关机时间然后直接提交即可&#xff0c;下班就可以…

什么是JWT及在JAVA中如何使用?

目录 1、为什么使用JWT? 2、JWT 的 格式 3、使用 JWT 就绝对安全 吗&#xff1f; 4、JWT 的 鉴权 流程 5、JWT 入门案例 5.1 引入依赖 5.2 生成Token 5.3 解析Token 5.4 工具类 JSON Web token简称JWT&#xff0c; 是用于对应用程序上的用户进行身份验证的标记。 也就…

测试中台初始设置

1.0 测试资源池 存在测试资源之后&#xff0c;将测试资源进行统一的管理&#xff0c;针对不同的测试场景和目的来定义不同的测试环境。将测试资源进行组合&#xff0c;测试平台可以对测试资源池进行统一的功能。 2.0 可用性测试及冒烟测试 可用性测试和冒烟测试都是快速验证…