【数据结构】排序(上)

在这里插入图片描述
个人主页~

堆排序看这篇~
还有这篇~


排序

  • 一、排序的概念及应用
    • 1、概念
    • 2、常见的排序算法
  • 二、常见排序的实现
    • 1、直接插入排序
      • (1)基本思想
      • (2)代码实现
      • (3)时间复杂度
      • (4)空间复杂度
    • 2、希尔排序
      • (1)基本思想
      • (2)代码实现
      • (3)时间复杂度
      • (4)空间复杂度
    • 3、选择排序
      • (1)基本思想
      • (2)代码实现
      • (3)时间复杂度
      • (4)空间复杂度
    • 4、堆排序
      • (1)基本思想
      • (2)代码实现
      • (3)时间复杂度
      • (4)空间复杂度
    • 5、冒泡排序
      • (1)基本思想
      • (2)代码实现
      • (3)时间复杂度
      • (4)空间复杂度
    • 6、快速排序
      • (1)基本思想
      • (2)代码实现
        • ①hoare版本
        • ②挖坑法版本
        • ③前后指针版本
      • (3)时间复杂度
      • (4)空间复杂度

一、排序的概念及应用

1、概念

排序就是按照某一关键字递增和递减排列起来的操作
排序在生活中非常常用,成绩、排行等等一切跟数字字母等有关的都能够排序

2、常见的排序算法

常见的排序算法有
插入排序:直接插入排序,希尔排序
选择排序:选择排序,堆排序
交换排序:冒泡排序、快速排序
归并排序:归并排序

二、常见排序的实现

1、直接插入排序

(1)基本思想

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

(2)代码实现

//我们看做一个一个插入
void InsertSort(int* a, int n)
{
	for (int i = 1; i < n; i++)
	{
	//从0到end都有序,tmp插入排序
		int end = i - 1;//end存储插入前的最后一个元素的下标,也就是第i-1个数据
		int tmp = a[i];//tmp是插入的数据,也就是第i个数据
		while (end >= 0)
		{
			if (a[end] > tmp)
			{
				a[end + 1] = a[end];
				end--;
			}//如果前边比后边大,就交换并且--end,继续向前比较
			else
			{
				break;//直到后边比前边大
			}
		}
		a[end + 1] = tmp;//将此时end+1下标的位置赋值tmp,后边的数据全都往后移了一位
	}
}

封装一个打印数组的函数

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

在这里插入图片描述

(3)时间复杂度

如果按照最坏的情况:
第一次排不需要比较
第二次需要比较一次
第三次需要比较两次

第N次需要比较N-1次
F(N)=0+1+2+3+…+N-1 = (N-1)*(N)/2
所以直接插入排序的最坏时间复杂度为O(N^2)
最好时间复杂度就是有序数组O(N)
所以直接插入排序是介于它们之间的,这才有了希尔排序这种优化算法,降低了时间复杂度

(4)空间复杂度

没有占用额外空间,O(1)

2、希尔排序

(1)基本思想

希尔排序可以说是高级的直接插入排序,它是希尔通过观察和实践在直接插入排序的基础上进行算法优化,将时间复杂度降低
希尔排序分为两步:
第一步:预排序,是将无序的数组排序至接近有序
第二步:直接插入排序
当gap越小越接近有序,gap越大预排序的速度会更快
当gap为1时,就是直接插入排序
简单来说希尔排序就是粗排后细排

(2)代码实现

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		//分为三组,最后加一可以保证最后一次的while循环gap等于1,相当于直接插入排序
		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;
			}
			//同直接插入排序,差别是分了组,每次要对比的数字的下标差了gap
			a[end + gap] = tmp;
		}
	}
}

在这里插入图片描述

(3)时间复杂度

希尔排序的时间复杂度并不好计算,因为gap的取值很多,我们没办法通过简单的计算来得出结果,这是一个数学上的难以解答的问题,资料中显示,它的时间复杂度在O(n^1.25)到O(1.6*n ^1.25)之间,我们粗略表示成O(n ^1.3)

(4)空间复杂度

没有占用额外空间,O(1)

3、选择排序

(1)基本思想

遍历数组,每一次将最大和最小的数据挑出来放到数列的起始和末尾位置,知道所有元素全部排完
这是一种超级慢的排序方式,实际使用中很少用

(2)代码实现

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
	
}

void SelectSort(int* a, int n)
{
	int right= n - 1;//定位到数组最后一个元素下标
	int left = 0;//定位到数组第一个元素下标
	while (left < right)
	{
		int min = left;//先将left作为最开始的最小值
		int max = right;//先将right作为最开始的最大值
		for (int i = left; i <= right; i++)
		{
			if (a[i] < a[min])
				min = i;
			if (a[i] > a[max])
				max = i;
		}
		//在left和right之间选出最大和最小的数
		Swap(&a[left], &a[min]);//交换a[left]与a[min]
		if (left == max)
			max = min;//这里注意,当最大值与left重叠时,将位置修正再交换
		Swap(&a[right], &a[max]);
		left++;
		right--;
	}
}

在这里插入图片描述

(3)时间复杂度

它的时间复杂度就是等差数列求和,可以很容易的看出来它的时间复杂度为O(N^2)

(4)空间复杂度

没有占用额外空间,O(1)

4、堆排序

在之前的文章中有详细的解释,我们可以来到二叉树-堆文章中来详细了解

(1)基本思想

利用堆的特性,即小堆堆顶最小,大堆堆顶最大的性质,来进行升序或降序排序

(2)代码实现

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

void AdjustDown(int* a, int n,int parent)
{
	int child = parent * 2 + 1;
	while(child < n)
	{
		if (child + 1 < n && a[child] < a[child + 1])
		{
			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)
{
    //parent = (child-1) / 2,i的初始值是最后一个元素的父节点
	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--;
	}
//每次调整都会将最大的一个数放在当前调整范围的最后一个位置,而调整范围的最后一个位置每次都会向前一个,直到成为升序数组
}

在这里插入图片描述

(3)时间复杂度

在前面链接中的文章中我们计算过它的时间复杂度,O(N*log₂N)

(4)空间复杂度

没有额外申请空间,O(1)

5、冒泡排序

(1)基本思想

冒泡排序是我们初识C语言时的接触到的第一个排序方式,也可以说是最鸡肋的排序方式,简单易懂但效率很低,就是两两元素相互比较,大的往后移动,遍历一遍下来最大的就到最后了,以此类推实现排序
这里我就不过多解释了

(2)代码实现

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			if (a[j] > a[j + 1])
			{
				Swap(&a[j], &a[j + 1]);
			}
		}
	}
}

(3)时间复杂度

相当于等差数组求和,n-1 + n-2 + … + 1
F(N) = (N-1+1)/2 * N/2
时间复杂度为O(N^2)

(4)空间复杂度

没有占用额外空间,空间复杂度为O(1)

6、快速排序

(1)基本思想

任取待排序序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素小于基准值,右子序列中所有元素大于基准值,然后在左右子序列中重复该过程,直到排序完成
这里我们每一次取的基准值都是左数第一个元素

(2)代码实现

①hoare版本
int PartSort1(int* a, int left, int right)
{
	int keyi = left;//将最左边的元素作为关键元素,即基准值,记录它的下标
	while (left < right)
	{
		while (left < right && a[keyi] <= a[right])
		{
			right--;
		}
		while (left < right && a[keyi] >= a[left])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
//左右两边向中间逼近,在右边找到小于基准值的数字,在左边找到大于基准值的数字,两者交换
	}
	Swap(&a[keyi], &a[left]);//循环出来之后,说明left与right相遇了,也就是说此时的这个位置左边的数字全部比基准值小,右边的数字都比基准值大,将这个位置的数字与基准值位置的数字交换位置
	return left;//将此时的基准值返回
}

void QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;
	int keyi = PartSort1(a, left, right);
	//将区间分成三个部分:keyi,keyi左,keyi右
	QuickSort(a, left, keyi - 1);
	QuickSort(a, keyi + 1, right);
	//对剩下的区间继续排序
}

在这里插入图片描述
在这里插入图片描述

②挖坑法版本
int PartSort2(int* a, int left, int right)
{
	int key = a[left];//存下坑里的数字
	int hole = left;//把最左边的位置作为坑
	while (left < right)
	{
		while (left < right && a[right] >= key)
			right--;
		//找到a[right] < key的位置跳出循环
		a[hole] = a[right];
		hole = right;
		//把这个位置挖新坑,将坑里的值存起来
		while (left < right && a[left] <= key)
			left++;
		a[hole] = a[left];
		hole = left;
		//右边挖完左边挖,左边找大于基准值的
	}
	a[hole] = key;
	//将最开始存下来的基准值填到此时剩下的坑里
	return hole;
}


void QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;
	int keyi = PartSort2(a, left, right);
	//将区间分成三个部分:keyi,keyi左,keyi右
	QuickSort(a, left, keyi - 1);
	QuickSort(a, keyi + 1, right);
	//对剩下的区间继续排序
}

在这里插入图片描述
在这里插入图片描述

③前后指针版本
int PartSort3(int* a, int left, int right)
{
	int prev = left;//初始前指针为最左边的元素
	int cur = left + 1;//初始后指针为前指针的后一个元素
	int keyi = left;//存下前指针的下标作为基准值下标
	while (cur <= right)
	{
		while (a[cur] < a[keyi] && ++prev != cur)
			Swap(&a[cur], &a[prev]);
//如果后指针指向的数字大小小于基准值,并且前指针的后一个指针不为后指针,那么前后指针指向位置的值交换
//当后指针指向的值小于基准值时,前指针都会往后走(这里的知识涉及到逻辑语句的短路)
		cur++;//后指针往后走
	}
	Swap(&a[prev], &a[keyi]);
	keyi = prev;
	//最后前指针所指向元素的大小一定大于基准值,将他们交换,此时的基准值左边除了第一个都比它小,右边都比它大
	return keyi;
}


void QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;
	int keyi = PartSort3(a, left, right);
	//将区间分成三个部分:keyi,keyi左,keyi右
	QuickSort(a, left, keyi - 1);
	QuickSort(a, keyi + 1, right);
	//对剩下的区间继续排序
}

在这里插入图片描述

在这里插入图片描述

(3)时间复杂度

快速排序是一种类二叉树类型的排序,所以它的时间复杂度为O(N*log₂N),计算方法同二叉树

(4)空间复杂度

递归创建类二叉树,空间复杂度为O(log₂N)


下期再见~
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

eNSP学习——RIP的路由引入

目录 主要命令 原理概述 实验目的 实验内容 实验拓扑 实验编址 实验步骤 1、基本配置 2、搭建公司B的RIP网络 3、优化公司B的 RIP网络 4、连接公司A与公司B的网络 需要eNSP各种配置命令的点击链接自取&#xff1a;华为&#xff45;NSP各种设备配置命令大全PDF版_ensp…

Windows开始ssh服务+密钥登录+默认启用powershell

文章内所有的命令都在power shell内执行&#xff0c;使用右键单击Windows徽标&#xff0c;选择终端管理员即可打开 Windows下OpenSSH的安装 打开Windows power shell&#xff0c;检查SSH服务的安装状态。会返回SSH客户端和服务器的安装状态&#xff0c;一下是两个都安装成功的…

操作系统教材第6版——个人笔记5

3.2 单连续分区存储管理 3.2.1 单连续分区存储管理 单连续分区存储管理 每个进程占用一个物理上完全连续的存储空间(区域) 单用户连续分区存储管理固定分区存储管理可变分区存储管理 单用户连续分区存储管理 主存区域划分为系统区与用户区设置一个栅栏寄存器界分两个区域…

最新下载:Navicat for MySQL 11软件安装视频教程

软件简介&#xff1a; Navicat for MySQL 是一款强大的 MySQL 数据库管理和开发工具&#xff0c;它为专业开发者提供了一套强大的足够尖端的工具&#xff0c;但对于新用户仍然易于学习。Navicat For Mysql中文网站&#xff1a;http://www.formysql.com/ Navicat for MySQL 基于…

基于51单片机俄罗斯方块小游戏

基于51单片机俄罗斯方块游戏 &#xff08;仿真&#xff0b;程序&#xff09; 功能介绍 具体功能&#xff1a; 1.用LCD12864显示游戏界面&#xff1b; 2.用四个按键控制游戏&#xff08;左、右移、下移、翻转&#xff09;&#xff1b; 3.游戏规则和平时玩的俄罗斯方块一样&a…

web前端:作业二

<!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>/* 1.将ul的子l…

搭建电商项目||购物商城||APP|小程序|电商独立站系统如何接入JD商品

京东商品采集的步骤和应用场景可以归纳如下&#xff1a; 一、采集步骤 注册账号&#xff1a;首先&#xff0c;需要在京东开放平台注册一个开发者账号。创建应用&#xff1a;登录开放平台后&#xff0c;创建一个应用以获取API密钥和应用凭据。获取权限&#xff1a;根据所需的服…

Docker高级篇之轻量化可视化工具Portainer

文章目录 1. 简介2. Portainer安装 1. 简介 Portianer是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用于方便管理Docker环境&#xff0c;包括单机环境和集成环境。 2. Portainer安装 官网&#xff1a;https://www.portainer.io 这里我们使用docker命令安装&…

【力扣高频题】003.无重复字符的最长子串

前段时间和小米的某面试官聊天。因为我一直在做 算法文章 的更新&#xff0c;就多聊了几句算法方面的知识。 并且在聊天过程中获得了一个“重要情报”&#xff1a;只要他来面试&#xff0c;基本上每次的算法题&#xff0c;都会去考察关于 子串和子序列 的问题。 的确&#xf…

每日一题——Python实现PAT乙级1099 性感素数(举一反三+思想解读+逐步优化)

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 专业点评 时间复杂度分析 空间复杂度分析 综合点评 我要更强 优化点 …

私有化AI搜索引擎FreeAskInternet

什么是 FreeAskInternet FreeAskInternet 是一个完全免费、私有且本地运行的搜索聚合器&#xff0c;并使用 LLM 生成答案&#xff0c;无需 GPU。用户可以提出问题&#xff0c;系统将使用 searxng 进行多引擎搜索&#xff0c;并将搜索结果合并到ChatGPT3.5 LLM 中&#xff0c;并…

⌈ 传知代码 ⌋ 基于曲率的图重新布线

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

数据库索引压力测试

本实验测试数据库在有索引和五索引内容上的查询时间随着数据量级增长的变化 测试的表结构 使用一个菜单的数据库表&#xff0c;包括菜品的ID&#xff0c;菜品名和价格 CREATE TABLE Menu (dish_id int(6) unsigned zerofill NOT NULL AUTO_INCREMENT,dish_name varchar(255)…

高通Android开关机动画踩坑简单记录

1、下面报错有可能是selinux的原因 Read-only file system 2、接着push 动画 reboot之后抓取logcat出现 &#xff0c;以下这个报错。看着像是压缩格式有问题。 3、于是重新压缩一下报错没有再出现 &#xff0c;压缩格式默认是标准&#xff0c;这里必须要改成存储格式哈 4、修改…

matlab演示地月碰撞

代码 function EarthMoonCollisionSimulation()% 初始化参数earth_radius 6371; % 地球半径&#xff0c;单位&#xff1a;公里moon_radius 1737; % 月球半径&#xff0c;单位&#xff1a;公里distance 384400; % 地月距离&#xff0c;单位&#xff1a;公里collision_tim…

Signac|成年小鼠大脑 单细胞ATAC分析(2)

引言 在本教程中&#xff0c;我们将探讨由10x Genomics公司提供的成年小鼠大脑细胞的单细胞ATAC-seq数据集。本教程中使用的所有相关文件均可在10x Genomics官方网站上获取。 本教程复现了之前在人类外周血单核细胞&#xff08;PBMC&#xff09;的Signac入门教程中执行的命令。…

【linux】进程控制——进程创建,进程退出,进程等待

个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 祝福语&#xff1a;愿你拥抱自由的风 相关文章 【Linux】进程地址空间-CSDN博客 【linux】详解linux基本指令-CSDN博客 目录 进程控制概述 创建子进程 fork函数 父子进程执行流 原理刨析 常见用法 出错原因 进程退出 概…

7-43 排列问题

排列问题 分数 10 全屏浏览 切换布局 作者 雷丽兰 单位 宜春学院 全排列问题 输出自然数1至n中n个数字的全排列&#xff08;1≤n≤9&#xff09;&#xff0c;要求所产生的任一数字序列中不允许出现重复的数字。 输入格式: 一个自然数 输出格式: 由1到n中n个数字组成的…

Python魔法之旅专栏(导航)

目录 推荐阅读 1、Python筑基之旅 2、Python函数之旅 3、Python算法之旅 4、博客个人主页 首先&#xff0c;感谢老铁们一直以来对我的支持与厚爱&#xff0c;让我能坚持把Python魔法方法专栏更新完毕&#xff01; 其次&#xff0c;为了方便大家查阅&#xff0c;我将此专栏…

freertos中的链表1 - 链表的数据结构

1.概述 freertos中链表的实现在 list.c 和 list.h。旨在通过学习freertos中的链表的数据结构&#xff0c;对freertos中的链表实现有一个整体的认识。freertos使用了三个数据结构来描述链表&#xff0c;分别是&#xff1a;List_t&#xff0c; MiniListItem_t&#xff0c;ListIt…