【数据结构】排序(上)

news2024/11/15 13:57:26

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

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


排序

  • 一、排序的概念及应用
    • 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)


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

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

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

相关文章

Java日期类Date、SimpleDateFormat 日期格式类、Calendar详细介绍

目录 一、Date类1.1 Date类简单介绍1.2 Date类的构造方法代码演示 二、SimpleDateFormat 日期格式化类2.1 SimpleDateFormat 日期格式化类简单介绍2.2 构造方法代码演示 日期格式化模板常用方法代码演示注意 三、Calendar类3.1 简单介绍3.2 创建对象代码演示 3.3 静态常量3.4 常…

Idea-Linux远程开发部署

第一步&#xff1a;File->Remote Development 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a;在Host位置填写Linux虚拟机的IP地址&#xff0c;在Username、Password填写对应的账号密码后点击Test Connection测试连接。 第五步&#xff1a; 第六步&#xff1a;在…

iCloud完全指南:释放Apple云服务的终极潜力

iCloud是苹果公司提供的云服务&#xff0c;它允许用户存储和同步照片、文档、音乐、应用数据以及更多类型的文件。通过有效利用iCloud&#xff0c;用户可以在不同设备间无缝地访问和编辑内容。本文旨在全面介绍如何高效使用iCloud&#xff0c;确保您能够最大化这一服务的价值。…

【玩转C语言】第四讲--->操作符与循环语句

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 引言&#xff1a; 大家好&#xff0c;我是坊钰&#xff0c;为了让大家深入了解C语言&#xff0c;我开创了【玩转C语言系列】&#xff0c;将为大家介绍C语言相关知识…

C语言 | Leetcode C语言题解之第142题环形链表II

题目&#xff1a; 题解&#xff1a; struct ListNode* detectCycle(struct ListNode* head) {struct ListNode *slow head, *fast head;while (fast ! NULL) {slow slow->next;if (fast->next NULL) {return NULL;}fast fast->next->next;if (fast slow) {s…

vue2的form利用插槽修改错误提示UI

1. 需求 很多时候我们使用el-form想修改下错误提示的UI&#xff0c;比如table中使用form校验这类场景下错误提示的UI调整就非常重要。 2. 了解文档 Form-Item Scoped Slot name说明error自定义表单校验信息的显示方式&#xff0c;参数为 { error } 3.实际使用 html里使用…

【List,ArrayList与顺序表】

目录 1&#xff0c;什么是List 2&#xff0c;List的使用 3&#xff0c;线性表 4&#xff0c;顺序表 4.1 接口的实现 5&#xff0c; ArrayList简介 6&#xff0c;ArrayList的使用 6.1 ArrayList的构造方法 6.2 ArrayList的常见操作 6.3 ArrayList的遍历 7&#xff0c;…

Layui实现下拉多选功能

1、问题概述? 提供源码下载 在项目中有很多地方需要使用到下拉框,并且实现选择多个信息,下面是展示。 支持如下功能: 1、分页 2、主题自定义 3、国际化 4、下拉方向 5、Tips修改等 6、Style自定义样式 7、取值 8、赋值 2、资源准备及测试? 2.1、资源下载

使用Puppeteer生成echarts图片

Puppeteer简介 Puppeteer 是一个用于控制 Headless Chrome 或 Chromium 浏览器的 Node.js 库。它提供了一个高层次的 API&#xff0c;能够让你以编程方式操作浏览器&#xff0c;从而实现自动化任务&#xff0c;比如生成页面截图和 PDF、抓取网页内容、自动化表单提交、UI 测试…

C++ 贪心算法——跳跃游戏、划分字母区间

一&#xff1a;跳跃游戏 55. 跳跃游戏 题目描述&#xff1a;给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1…

【服务实现读写分离】

文章目录 什么是读写分离基于Spring实现实现读写分离项目中常用的数据源切换依赖包 什么是读写分离 服务读写分离&#xff08;Service Read-Write Splitting&#xff09;是一种常见的数据库架构设计模式&#xff0c;旨在提高系统的性能和可扩展性。通过将读操作和写操作分离到…

借助ChatGPT快速仿写一篇优质论文,无痛仿写、完美创作

大家好&#xff0c;感谢关注。我是七哥&#xff0c;一个在高校里不务正业&#xff0c;折腾学术科研AI实操的学术人。可以添加我&#xff08;yida985&#xff09;交流学术写作或ChatGPT等AI领域相关问题&#xff0c;多多交流&#xff0c;相互成就&#xff0c;共同进步 在学术写…

探索智慧景区票务系统的架构与应用

随着旅游业的迅速发展&#xff0c;智慧景区票务系统已经成为提升景区管理效率、优化游客体验的重要工具。智慧景区票务系统的架构设计与应用&#xff0c;将现代信息技术与景区管理相结合&#xff0c;为景区的门票销售、入园管理和游客服务提供了全新的解决方案。本文将深入探讨…

形参和实参的区别

形参&#xff1a;函数定义时声明的参数。 实参&#xff1a;调用函数时传递的参数。

数字孪生智慧水利:精准管理与智能决策的新时代

图扑数字孪生技术在智慧水利中的应用&#xff0c;通过虚拟模型与真实水利系统的无缝连接&#xff0c;实现对水资源和水利工程的全面监控和精细管理。实时数据采集与动态模拟提升了水利系统的预测和响应能力&#xff0c;从洪水预警到水质监测&#xff0c;数字孪生助力各项决策更…

一款开源文件加速下载利器

前言 大文件的下载&#xff0c;浏览器支持不是很好&#xff0c;今天下载了一个20个G的文件&#xff0c;连续失败了好多次。 然后寻找到了一个开源的下载工具gospeed&#xff0c;可以完美的解决这个问题。而且下载速度快。 简介 Gopeed&#xff08;全称 Go Speed&#xff09;&am…

k8s面试题大全,保姆级的攻略哦(三)

目录 1、简述ETCD及其特点? 2、简述ETCD适应的场景? 3、简述什么是Kubernetes? 4、简述Kubernetes和Docker的关系? 5、简述Kubernetes中什么是Minikube、Kubectl、Kubelet? 6、简述Kubernetes常见的部署方式? 7、简述Kubernetes如何实现集群管理? 8、简述Kubern…

2 - 寻找用户推荐人(高频 SQL 50 题基础版)

2.寻找用户推荐人 考点: sql里面的不等于&#xff0c;不包含null -- null 用数字判断筛选不出来 select name from Customer where referee_id !2 OR referee_id IS NULL;

RK3288 android7.1 实现ota升级时清除用户数据

一&#xff0c;OTA简介(整包&#xff0c;差分包) OTA全称为Over-The-Air technology(空中下载技术)&#xff0c;通过移动通信的接口实现对软件进行远程管理。 1. 用途&#xff1a; OTA两种类型最大的区别莫过于他们的”出发点“&#xff08;我们对两种不同升级包的创建&…