【C语言】数据结构——排序(一)

news2024/11/28 7:45:56

💗个人主页💗
⭐个人专栏——数据结构学习⭐
💫点击关注🤩一起学习C语言💯💫

目录

  • 导读:
  • 数组打印与交换
  • 1. 插入排序
    • 1.1 直接插入排序
      • 1.1.1 基本思想
      • 1.1.2 实现代码
      • 1.1.3 图解
    • 1.2 希尔排序
      • 1.2.1 基本思想
      • 1.2.2 实现代码
      • 1.2.3 图解
  • 2. 选择排序
    • 2.1 直接选择排序
      • 2.1.1 基本思想
      • 2.1.2 实现代码
      • 2.1.3 图解
    • 2.2 堆排序
      • 2.2.1 基本思想
      • 2.2.2 实现代码
      • 图解

导读:

我们在前面学习了单链表,顺序表,栈和队列,小堆,链式二叉树。
今天我们来学习排序,包括直接插入排序,希尔排序,选择排序,堆排序,冒泡排序,快排以及归并排序。
冒泡排序,快排和归并排序我们放在之后分析,今天主要来分析前面的。
关注博主或是订阅专栏,掌握第一消息。

数组打印与交换

为了方便我们来测试每个排序是否完成,我们来写个打印数组和交换两数的函数,方便我们后续的打印与交换。

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

1. 插入排序

插入排序分为直接插入排序和希尔排序。
基本思想:

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。

1.1 直接插入排序

1.1.1 基本思想

直接插入排序是一种简单直观的排序算法,其基本思想是将待排序的元素一个个插入到已排好序的部分中,从而逐步构建已经排序的序列。

  1. 首先,将待排序序列的第一个元素视为已排序部分,可以认为该部分只包含一个元素。
  2. 然后,依次将后面的元素插入到已排序部分中的正确位置。假设要插入的元素为A,那么从已排序部分的最后一个元素开始向前遍历,如果当前元素大于A,则将当前元素后移一位;反之,找到A应该插入的位置,将其插入到该位置。
  3. 重复步骤2,直到所有的元素都被插入到已排序部分中。

直接插入排序的时间复杂度为O(n^2),其中n为待排序序列的长度。辅助空间复杂度为O(1),是一种稳定的排序算法。

1.1.2 实现代码

void InsertSort(int* a, int n)
{
	int i = 0;
	for (i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		// end往前移动
		while (end >= 0)
		{
			// 如果tmp小于a[end] 让a[end]往后移动
			//也就是移动到end+1的位置
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		//循环结束 此时找到了比tmp小的数值 
		//把tmp的值放在其后面
		a[end + 1] = tmp;
	}
}

void TestInsertSort()
{
	int arr[10] = { 5, 1, 8, 7, 3, 9, 2, 0, 4, 6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	InsertSort(arr, 10);
	//打印
	PrintArray(arr, 10);
}
int main()
{
	TestInsertSort();
	return 0;
}

1.1.3 图解

在这里插入图片描述

1.2 希尔排序

1.2.1 基本思想

希尔排序(Shell Sort)是一种排序算法,也被称为缩小增量排序。
基本思想是将待排序的数组分成若干个子序列,分别进行插入排序,然后逐步缩小增量,直到增量为1时进行最后一次排序。希尔排序的关键在于如何选择增量序列

  1. 初始化增量序列,通常取数组长度的一半作为初始增量,然后每次再缩小增量为前一次的一半。
  2. 对每个增量序列进行插入排序,从增量序列的第一个元素开始,将其与对应增量序列的后续元素逐个比较并插入到正确的位置。
  3. 缩小增量,重复步骤2,直到增量为1。
  4. 最后一次插入排序完成后,数组已经基本有序,但并不是完全有序的,此时再进行一次插入排序,将数组完全排序。

希尔排序的优点是相对于简单插入排序来说,移动的元素较少,比较次数也相对较少,可以提高排序效率。但是希尔排序的时间复杂度依赖于增量序列的选择,不同的增量序列可能会导致不同的排序效果。

1.2.2 实现代码

void ShellSort(int* a, int n)
{
	int gap = n;
	//gap > 1 时是预排序,目的让他接近有序
	//gap == 1是直接插入排序,目的是让他有序
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		int i = 0;
		//特别主要i的范围,避免越界
		for (i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

void TestShellSort()
{
	int arr[10] = { 5, 1, 8, 7, 3, 9, 2, 0, 4, 6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	ShellSort(arr, 10);
	PrintArray(arr, 10);
}

int main()
{
	TestShellSort();
	return 0;
}

1.2.3 图解

在这里插入图片描述

2. 选择排序

选择排序可以分为直接选择排序和堆排序。
基本思想:

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

2.1 直接选择排序

2.1.1 基本思想

直接选择排序算法的基本思想是找到待排序序列中的最小(或最大)元素,将它与序列的第一个元素交换位置,然后再从剩下的未排序序列中找到最小(或最大)元素,与序列的第二个元素交换位置,依次类推,直到整个序列排好序为止。

  1. 遍历待排序序列,假设第一个元素为当前最小(或最大)元素。
  2. 从当前元素的下一个位置开始遍历,如果找到比当前元素更小(或更大)的元素,则将其索引记为最小(或最大)元素的索引。
  3. 遍历完成后,将最小(或最大)元素与当前元素交换位置。
  4. 重复以上步骤,直到整个序列排好序为止。

直接选择排序算法的时间复杂度为O(n^2),其中n为序列的长度。

2.1.2 实现代码

void SelectSort(int* a, int n)
{
	int begin = 0;
	int end = n - 1;
	while (begin < end)
	{
		int min = begin;
		int max = begin;
		for (int i = begin + 1; i <= end; i++)
		{
			if (a[i] < a[min])
			{
				min = i;
			}
			if (a[i] > a[max])
			{
				max = i;
			}
		}
		// 把最小的值移到最前面
		Swap(&a[begin], &a[min]);
		// 如果max=begin代表第一个元素为最大值
		// 但是经过上个语句已经把第一个元素和最小的元素交换
		if (max == begin)
		{
			max = min;
		}
		//最大值放到后面
		Swap(&a[end], &a[max]);
		++begin;
		--end;
	}
}
void TestSelectSort()
{
	int arr[10] = { 5, 1, 8, 7, 3, 9, 2, 0, 4, 6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	SelectSort(arr, 10);
	PrintArray(arr, 10);
}

int main()
{
	TestSelectSort();
	return 0;
}

2.1.3 图解

在这里插入图片描述

2.2 堆排序

2.2.1 基本思想

堆排序算法的基本思想是利用堆的数据结构进行排序。堆是一种特殊的树状数据结构,分为大顶堆和小顶堆两种类型。
对于大顶堆,父节点的值大于或等于子节点的值;对于小顶堆,父节点的值小于或等于子节点的值。堆排序利用堆的性质,使得每次操作的时间复杂度为O(log n)。

  1. 构建初始堆:将待排序序列构建成一个大顶堆。
  2. 将堆顶元素(最大值)与堆的最后一个元素交换位置,然后堆的大小减1。这样就将最大值移到了序列的末尾。
  3. 对新的堆顶元素进行向下调整(即将堆的根节点调整到正确的位置),使得剩余的元素重新构建成一个大顶堆。
  4. 重复步骤2和步骤3,直到堆的大小为1,即完成排序。

主要思想是通过不断地交换堆顶元素和堆的最后一个元素,将最大值依次放到序列的末尾,同时不断地调整堆结构,使得剩余元素保持堆的性质。最后得到的序列就是按照从小到大排序的结果。
堆排序的时间复杂度是O(nlogn),其中n是序列的大小。堆排序是一种不稳定的排序算法,因为在构建大顶堆的过程中,相同大小的元素可能会交换位置。

2.2.2 实现代码

我们之前有实现过堆,所以我们在这里直接引用即可。

// 向下调整
void AdjustDown(int* a, int size, int parent)
{
	int child = parent * 2 + 1;

	while (child < size)
	{
		// 假设左孩子大,如果解设错了,更新一下
		if (child + 1 < size && a[child + 1] < a[child])
		{
			++child;
		}
		// 交换父节点和子节点
		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--)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	// 遍历循环
	while (end > 0)
	{
		// 将当前最大元素(堆顶)放到数组末尾
		Swap(&a[0], &a[end]);
		// 重新调整剩余元素构建的堆
		AdjustDown(a, end, 0);
		--end;
	}
}
void TestHeapSort()
{
	int arr[10] = { 5, 1, 8, 7, 3, 9, 2, 0, 4, 6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	HeapSort(arr, sz);
	PrintArray(arr, 10);
}
int main()
{
	TestHeapSort();
	return 0;
}

图解

在这里插入图片描述

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

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

相关文章

计算机视觉与自然语言处理(Open AI)

1.语音识别技术 语音识别是将语音转换为文本的技术&#xff0c; 是自然语言处理的一个分支。通过特征的提取、模式的匹配将语音信号变为文本或命令&#xff0c;以实现机器识别和理解语音。 按照应用场景的不同&#xff0c;可以大致分为三类&#xff1b; • 电信级系统应用&…

HBase基础知识(四):HBase API

HBase还提供了API&#xff0c;我们可以通过编程的方式来进行对HBase的操作。 1. 环境准备 新建项目后在 pom.xml 中添加依赖&#xff1a; <dependencies><dependency><groupId>org.apache.hbase</groupId><artifactId>hbase-server</artifa…

首次使用TypeScript,报错:无法重新声明块级范围变量(声明变量报错)

前几天在书写TypeScript代码时&#xff0c;出现了声明变量报错的情况&#xff0c;具体情况如下&#xff1a; let arr: number; arr 10; console.log(arr);报错如下&#xff1a; 解决方案&#xff1a; 在配置文件tsconfig.json中&#xff0c;配置如下代码&#xff1a; { &q…

STM32F407-14.3.10-表73具有有断路功能的互补通道OCx和OCxN的输出控制位-1x000-1x111(总结)

基于表73中&#xff0c;主输出使能&#xff08;MOE1&#xff09;的8种OCx与OCxN的输出状态及波形图&#xff0c;已经单独整理输出8篇文章&#xff0c;方便需要时单独回查。 主输出使能时&#xff08;MOE1&#xff09;总结如下 通过表73中可得以下结论 1、控制位1x000与1x100…

[玩转AIGC]LLaMA2之如何跑llama2.c的chat模式

前言&#xff1a;之前我们关于llama2的相关内容主要停留在gc层面&#xff0c;没介绍chat模式&#xff0c;本文将简单介绍下llama2.c的chat模式如何跑起来。训练就算了&#xff0c;没卡训练不起来的&#xff0c;但是用CPU来对别人训练好的模型进行推理还是绰绰有余的&#xff0c…

2023-12-29 服务器开发-centos-安装php8

摘要: 2023-12-29 服务器开发-centos-安装php8 centos-安装php8 必备条件 Minimal CentOS 8 / RHEL 8User with sudo rightsInternet Connection (1) 更新系统 更新系统 $ sudo dnf update $ sudo dnf upgrade 重启系统 $ sudo reboot (2) 启用 EPEL & Remi 软件库…

Pycharm引用其他文件夹的py

Pycharm引用其他文件夹的py 方式1&#xff1a;包名设置为Sources ROOT 起包名的时候&#xff0c;需要在该文件夹上&#xff1a;右键 --> Mark Directory as --> Sources ROOT 标记目录为源码目录&#xff0c;就可以了。 再引用就可以了 import common from aoeweb impo…

lv13 内核模块动态添加新功能 6

1 动态加载法 即新功能源码与内核其它源码不一起编译&#xff0c;而是独立编译成内核的插件(被称为内核模块&#xff09;文件.ko 1.1 新功能源码与Linux内核源码在同一目录结构下时 给新功能代码配置Kconfig&#xff08;模块代码与上一级相同&#xff09; 给新功能代码改写…

2023年12月28日学习记录

目录 1、今日计划学习内容2、今日学习内容文献阅读—A Data-driven Base Station Sleeping Strategy Based on Traffic Prediction0、选这篇文章的原因1、文章的主要内容和贡献2、使用的数据集3、结果及分析4、郭郭有话说 整理流量预测的代码 3、今日学习总结 1、今日计划学习内…

[枚举涂块]画家问题

画家问题 题目描述 有一个正方形的墙&#xff0c;由N*N个正方形的砖组成&#xff0c;其中一些砖是白色的&#xff0c;另外一些砖是黄色的。Bob是个画家&#xff0c;想把全部的砖都涂成黄色。但他的画笔不好使。当他用画笔涂画第(i, j)个位置的砖时&#xff0c; 位置(i-1, j)、…

【ES】Elasticsearch常见问题与解决(持续更新)

目录 Elasticsearch常见问题 1. 集群健康问题 2. 性能问题 3. 映射问题 4. 分片问题 5. 内存问题 6. 硬件问题 7. 配置问题 8. 安全问题 9. 网络问题 10. 版本不兼容 Elasticsearch日常使用小结 【Q】离线告警&#xff0c;有IP已离线 【Q】统计某个应用的某个索引…

tcp 乱序度量与丢包标记

传统 tcp 以序列号差度量乱序&#xff0c;比如 1, 2, 3, 4, 6, 7, 8, 5 这个序列的 5 延后了 3 个段&#xff0c;就称这个序列的乱序度为 3。 如果乱序度为 m&#xff0c;则序列 n, n 1 k, n 1 k r, …, n 1 k r x 中&#xff0c;只要 (n 1 k r x) - (n 1) k …

maven中dependencyManagement标签

简介 dependencyManagement正如其名&#xff0c;用于项目依赖的统一管理。 在父项目中的pom.xml文件中加入dependencyManagement标签即可完成依赖版本的声明。在声明完成后&#xff0c;子项目&#xff08;module&#xff09;中引用相同的依赖时可以不指定version标签自动引入…

音频修复和增强软件:iZotope RX 10 (Win/Mac)中文汉化版

iZotope RX 是一款专业的音频修复和增强软件&#xff0c;一直是电影和电视节目中使用的行业标准音频修复工具&#xff0c;iZotope能够帮助用户对音频进行制作、后期合成处理、混音以及对损坏的音频进行修复&#xff0c;再解锁更多功能之后还能够对电影、游戏、电视之中的音频进…

使用css实现 Typora markdown 标题自动编号

第一&#xff0c;找到主题文件夹 第二&#xff0c;复制下面代码放入 AutoNumber.css文件中 body {counter-reset: h1; }#write h1, .markdown-section h1 {counter-reset: h2; }#write h2, .markdown-section h2 {counter-reset: h3; }#write h3, .markdown-section h3 {counte…

YOLOv5改进 | 2023注意力篇 | FocusedLinearAttention聚焦线性注意力

一、本文介绍 本文给大家带来的改进机制是FLAttention&#xff08;聚焦线性注意力&#xff09;是一种用于视觉Transformer模型的注意力机制(但是其也可以用在我们的YOLO系列当中从而提高检测精度)&#xff0c;旨在提高效率和表现力。其解决了两个在传统线性注意力方法中存在的…

网络攻防中应该掌握的进阶工具udp2raw,通过raw socket给UDP包加上TCP或ICMP header,进而绕过UDP屏蔽或QoS

网络攻防中应该掌握的进阶工具udp2raw,通过raw socket给UDP包加上TCP或ICMP header,进而绕过UDP屏蔽或QoS。 udp2raw tunnel,通过raw socket给UDP包加上TCP或ICMP header,进而绕过UDP屏蔽或QoS,或在UDP不稳定的环境下提升稳定性。可以有效防止在使用kcptun或者finalspeed的…

企业级依赖管理: 深入解读 Maven BOM

一、背景 当开发者在一个大型项目中使用 Maven 进行依赖管理时&#xff0c;项目往往会包含多个模块或子项目&#xff0c;并且这些模块会共享相同的依赖项。但是&#xff0c;不同模块可能会独立地指定各自的依赖版本&#xff0c;这可能导致以下问题&#xff1a; 依赖版本不一致…

Activiti工作流框架学习笔记(二)之springboot2.0整合工作流Activiti6.0

文/朱季谦 以前在工作当中做过不少与工作流Activiti有关的工作&#xff0c;当时都是spring集成activiti5.22的项目&#xff0c;现在回过头去看&#xff0c;其实版本已经稍微老了&#xff0c;因此&#xff0c;基于先前的工作经验&#xff0c;决定用较新版本的技术来重新梳理下以…

字符串转成时间的SQL,一个多种数据库通用的函数

select date 2010-10-06 from dual; date 函数&#xff0c;此函数适用于&#xff1a; 1.MySQL数据库 2.Oracle数据库 3.达梦数据库 4.人大金仓数据库