【数据结构】排序(下)

在这里插入图片描述
个人主页~
排序(上)
栈和队列


排序

  • 二、常见排序的实现
    • 8、快速排序的优化
    • 9、非递归快速排序
      • (1)基本思想
      • (2)代码实现
      • (3)时间复杂度
      • (4)空间复杂度
    • 10、归并排序
      • (1)基本思想
      • (2)代码实现
      • (3)时间复杂度
      • (4)空间复杂度
    • 11、非递归归并排序
      • (1)基本思想
      • (2)代码实现
      • (3)时间复杂度
      • (4)空间复杂度
    • 12、非比较排序
      • (1)基本思想
      • (2)代码实现
      • (3)时间复杂度
      • (4)空间复杂度
  • 三、各个排序方法所用时间的比较
    • 1、代码实现
    • 2、分析
  • 四、各个排序的稳定性
    • 1、基本概念
    • 2、各个排序的稳定性复杂度一览表

二、常见排序的实现

8、快速排序的优化

当我们使用快速排序时,最坏的情况就是数组有序,此时的时间复杂度为O(N^2)
最好的情况就是key每次取中位数
所以我们为了避免最坏情况的发生,我们在快速排序的基础上衍生了一种优化的方法叫做三数取中
还有一种方法是随机选key,但随机选key的效果不如三数取中

int GetMidIndex(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
			return mid;
		else if (a[left] < a[right])
			return right;
		else
			return left;
	}
	else
	{
		if (a[mid] > a[right])
			return mid;
		else if (a[left] > a[right])
			return right;
		else
			return left;
	}
}

将三个比较出中间的数字作为key然后换到left上,进行partsort
在每个partsort的最前边加上这条语句,就优化了这个快速排序的结构

int PartSort(int* a, int left, int right)
{
	int midi = GetMidIndex(a, left, right);
	Swap(&a[left], &a[midi]);
	......
}

9、非递归快速排序

(1)基本思想

前边我们讲的快速排序是基于递归条件下实现的,但我们知道,递归会消耗栈上的空间,并且栈上的空间比较小,不能实现大量数据的快速排序,所以我们要将这个过程放在空间更大的堆上,也就是使用栈来实现
栈的作用就是存储区间,这个区间由两个整数组成,通过出入栈来模拟递归的过程

(2)代码实现

这里需要包含一下以前我们写过的栈的头文件

void QuickSortNonR(int* a, int left, int right)
{
	Stack st;
	StackInit(&st);
	StackPush(&st,right);
	StackPush(&st, left);

	while (!StackEmpty(&st))
	{
		int left = StackTop(&st);
		StackPop(&st);
		int right = StackTop(&st);
		StackPop(&st);
        //取出区间
        
		int keyi = PartSort1(a, left, right);
		//通过keyi将数据区间一分为二
		
		if (keyi + 1 < right)
		{
			StackPush(&st, right);
			StackPush(&st, keyi + 1);
		}
		if (left < keyi - 1)
		{
			StackPush(&st, keyi - 1);
			StackPush(&st, left);
		}
		//存入区间
	}
	StackDestroy(&st);
}

在这里插入图片描述

(3)时间复杂度

同递归方式的快速排序,为O(log₂N * N)

(4)空间复杂度

同递归方式的快速排序,为O(log₂N)

10、归并排序

(1)基本思想

将一个待排序的序列分为若干个子序列,每个子序列都是有序的,然后再将有序的序列合并为整体的有序序列

(2)代码实现

void _MergeSort(int* a, int left, int right, int* tmp)
{
	if (left == right)
		return;
	
	//找到中间下标
	int midi = (left + right) / 2;
	
	//一分为二二分为四的分开
	_MergeSort(a, left, midi, tmp);
	_MergeSort(a, midi + 1, right, tmp);
	
	int begin1 = left, end1 = midi;
	int begin2 = midi + 1, end2 = right;
    
    //i用来记录容器数组中对应的下标
	int i = left;
	
	//将两个数组中按升序归并到容器数组中
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
			tmp[i++] = a[begin1++];
			
		else
			tmp[i++] = a[begin2++];
	}
  
    //如果左右两个区间的数字还没有全部入到容器数组中,将它们按顺序输入
	while (begin1 <= end1)
		tmp[i++] = a[begin1++];
	while (begin2 <= end2)
		tmp[i++] = a[begin2++];

    //将容器数组复制到原来的数组上
	memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
}

在这里插入图片描述

(3)时间复杂度

归并排序分为两个过程
一是分解过程,这是一个类二叉树的过程,由中间下标分为两个区间,再分为四个区间,以此类推,此过程的时间复杂度是O(log₂N)
二是合并过程,合并过程中需要遍历整个数组,找到谁大谁小然后排序,这个过程的时间复杂度是O(N)
整个过程的时间复杂度就是O(N*log₂N)

(4)空间复杂度

该过程需要在堆上开辟n个空间,以及递归所需要的log₂n个在栈上的空间,由于对于n来说log₂n很小,所以它的空间复杂度为O(N)

11、非递归归并排序

(1)基本思想

与快速排序相同,递归方式的归并排序需要使用栈中空间,在处理大量数据时空间不够,所以我们可以用循环的方法减少栈的使用,这就是非递归的归并排序

(2)代码实现

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	int gap = 1;
	while (gap < n)
	{
		int j = 0;//作为tmp的下标
		for (int i = 0; i < n; i += 2*gap)//每次跳过两组数据
		{
	    	//这里的间隔差gap,每次比较两组数据
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + gap * 2 - 1;
		
			//以下同上
			if (end1 >= n || begin2 >= n)
				break;
			if (end2 >= n)
				end2 = n - 1;
				
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
					tmp[j++] = a[begin1++];
				else
					tmp[j++] = a[begin2++];
			}
			
			while (begin1 <= end1)
				tmp[j++] = a[begin1++];
			while (begin2 <= end2)
				tmp[j++] = a[begin2++];
				
			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
		}
		gap *= 2;//while结束后把间隔调两倍
	}
	free(tmp);
}

在这里插入图片描述

(3)时间复杂度

for循环每次gap*=2,时间复杂度为O(log₂N),for循环中遍历了一遍数组,时间复杂度为O(N)
总的时间复杂度为O(N * log₂N)

(4)空间复杂度

申请了堆上的n个空间,空间复杂度为O(N)

12、非比较排序

(1)基本思想

计数排序是一种非比较排序,实现过程中不需要任何的比较
第一步:统计相同元素出现的次数
第二步:根据统计的结果将序列回收到原来的序列当中
这个排序适用于数据比较集中的序列

(2)代码实现

void CountSort(int* a, int n)
{
	int min, max;
	min = max = a[0];
	for (int i = 0; i < n; i++)
	{
		if (a[i] > max)
			max = a[i];
		if (a[i] < min)
			min = a[i];
	}
	int range = max - min + 1;
	//找到这一组数据中最大和最小的数相减得出这组数据的范围
	int* countA = (int*)malloc(sizeof(int) * range);
	memset(countA, 0, sizeof(int)*range);
	//创建一个在堆上的数组作为计数数组,大小为这组数据的范围,将其中的元素全部重置为0
	for (int i = 0; i < n; i++)
		countA[a[i] - min]++;
	//将每个数字出现的次数记录
	int k = 0;
	for (int i = 0; i < range; i++)
	{
		while (countA[i]--)
			a[k++] = i + min;
	}
}//下标加上整个数组的最小值就是当前数据的大小,countA为0时退出循环,不为0就记录下来

在这里插入图片描述

(3)时间复杂度

找出最大最小值需要遍历一遍数组,记录数字走for循环中range
所以时间复杂度为O(N+range),当数据比较集中时,时间复杂度接近O(N)
到底是O(N)还是O(range)取决于它们俩哪个大

(4)空间复杂度

在堆上开辟了range个空间,空间复杂度为O(range),当数据比较集中时,空间复杂度接近O(1)

三、各个排序方法所用时间的比较

1、代码实现

void TestOP()
{
	srand(time(0));
	const int N = 100000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);
	int* a5 = (int*)malloc(sizeof(int) * N);
	int* a6 = (int*)malloc(sizeof(int) * N);
	int* a7 = (int*)malloc(sizeof(int) * N);
	int* a8 = (int*)malloc(sizeof(int) * N);


	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();//取随机值
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];
		a7[i] = a1[i];
		a8[i] = a1[i];
		//赋值给所有数据
	}

	int begin1 = clock();
	InsertSort(a1, N);
	int end1 = clock();
//clock是一个函数,用于记录当前时间点,在开始时记录一下,在结束后记录一下
//得出的时间差就是这个排序所用的时间
	int begin2 = clock();
	ShellSort(a2, N);
	int end2 = clock();

	int begin3 = clock();
	BubbleSort(a3, N);
	int end3 = clock();

	int begin4 = clock();
	SelectSort(a4, N);
	int end4 = clock();

	int begin5 = clock();
	HeapSort(a5, N);
	int end5 = clock();

	int begin6 = clock();
	QuickSort(a6, 0, N - 1);
	int end6 = clock();

	int begin7 = clock();
	MergeSort(a7, N);
	int end7 = clock();

	int begin8 = clock();
	CountSort(a8, N);
	int end8 = clock();

	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("BubbleSort:%d\n", end3 - begin3);
	printf("SelcetSort:%d\n", end4 - begin4);
	printf("HeapSort:%d\n", end5 - begin5);
	printf("QuickSort:%d\n", end6 - begin6);
	printf("MergeSort:%d\n", end7 - begin7);
	printf("CountSort:%d\n", end8 - begin8);

	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
	free(a7);
	free(a8);
}

2、分析

在这里插入图片描述
当数据给到10W个时,我们可以明显看出各个排序的差距
最拉胯的就是冒泡排序,跟其他排序所用时间都不在一个量级上
然后就是直接插入以及选择插入
然后就是希尔排序、堆排序、快速排序、归并排序
因为随机数的生成是由时间戳实现的,两个随机数之间差的并不多,所以范围比较集中,这就使得计数排序超级快

四、各个排序的稳定性

1、基本概念

稳定性好就是一个序列中存在着两个即两个以上的相同数据,这两个数据在排序前后相对位置不变,反之就是不好
这里的前后相对位置不变不是指它们两个数据一直待在原来的位置,而是前边的数字a1在排列后还在后边的数字a2前边,而不是跑到它的后边了

2、各个排序的稳定性复杂度一览表

排序方法平均情况最好情况最坏情况辅助空间稳定性
冒泡排序O(N^2)O(N)O(N^2)O(1)稳定
简单选择排序O(N^2)O(N^2)O(N^2)O(1)不稳定
直接插入排序O(N^2)O(N)O(N^2)O(1)稳定
希尔排序O(N ^log₂N)~O(N ^2)O(N^1.3)O(N^2)O(1)不稳定
堆排序O(N^log₂N)O(N^log₂N)O(N^log₂N)O(1)不稳定
归并排序O(N^log₂N)O(N^log₂N)O(N^log₂N)O(N)稳定
快速排序O(N^log₂N)O(N^log₂N)O(N^2)O(log₂N)~O(N)不稳定

感谢观看
在这里插入图片描述

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

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

相关文章

kali中安装zsteg教程

1、下载文件 git clone http://www.github.com/zed-0xff/zsteg 2、第一步需要保证虚拟机是有网络的&#xff0c;不然无法克隆 3、可以将网络设置成如下后重启&#xff0c;访问百度看看能不能访问&#xff0c;若可以访问&#xff0c;则进行下一步 4、查看源&#xff0c;删除源&…

✊构建浏览器工作原理知识体系(网络协议篇)

🌻 前言 书接上回~ 系列文章目录: # ✊构建浏览器工作原理知识体系(开篇)# ✊构建浏览器工作原理知识体系(浏览器内核篇)# ✊构建浏览器工作原理知识体系(网络协议篇)✊构建浏览器工作原理知识体系(网页加载超详细全过程篇)为什么你觉得偶尔看浏览器的工作原理,…

程序员日志之计算机相关专业还值得选择吗?

目录 传送门正文日志1、概要2、专业选择2.1、专业2.2、学校2.3、城市 3、计算机相关专业还值得选择吗&#xff1f; 传送门 SpringMVC的源码解析&#xff08;精品&#xff09; Spring6的源码解析&#xff08;精品&#xff09; SpringBoot3框架&#xff08;精品&#xff09; MyB…

【会议】一张图片讲清楚:项目启动会议

另附上启动会前需要准备的内容&#xff1a;

【吉林大学Java程序设计】第10章:Java数据库编程技术(JDBC)

第10章&#xff1a;Java数据库编程技术&#xff08;JDBC&#xff09; 1. 数据库系统概述数据库系统SQL语言 2.JDBC概述JDBC APIJDBC Driver API 3.JDBC编程步骤示例1&#xff1a;MySQL数据库操作程序示例2&#xff1a;Java DB数据库操作程序 重点小结 1. 数据库系统概述 数据库…

当OpenHarmony遇上OpenEuler

1、 安装openEuler 虚拟机、物理机器当然都可以安装。虚拟机又可以使用WSL、或者VMWare、VirtualBox虚拟机软件&#xff0c;如果需要安装最新版本&#xff0c;建议使用后者。当前WSL只支持OpenEuler 20.03。 1.1 WSL openEuler WSL的安装都是程序员的必备技能了&#xff0c;…

掌控未来:用决策树算法揭秘胜利者的必胜策略!

掌控未来&#xff1a;用决策树算法揭秘胜利者的必胜策略&#xff01; 一、引言1.1. 决策树的定义1.2. 发展历程1.3. 当前应用概况1.4. 本文内容安排 二、决策树的基本概念2.1 节点和叶节点2.2 决策树的结构结构图示不同结构的决策树 三、决策树的算法原理3.1 基本思想3.2 核心算…

设计灵感源泉!7个令人赞叹的网页界面设计展示

网页的界面设计主要是指视觉设计和风格设计。高质量的界面更容易吸引用户的注意力&#xff0c;从而更准确地向用户传达信息。对于设计师来说&#xff0c;他们需要从高质量的作品中获得稳定的灵感&#xff0c;以帮助他们更高效地实现设计目标。在本文中&#xff0c;梳理了7个高质…

黄金价格与美元的关系变了?

在一些传统的定价框架中&#xff0c;现货黄金的价格走势取&#xff0c;决于美元的实际利率水平——实际利率越高&#xff0c;黄金价格越低&#xff0c;反之亦然。在大多数的时候&#xff0c;美元的实际利率决定了美元指数的高低所以人们通常认为&#xff0c;现货金价与美元呈反…

猫头虎推荐20个值得体验的通用大模型

猫头虎推荐20个值得体验的通用大模型 &#x1f680; 大家好&#xff0c;我是猫头虎&#xff0c;一名专注于科技领域的自媒体博主。今天是周一&#xff0c;新的开始&#xff0c;我们来深入探讨一下当前最值得体验的通用大模型。这些AI模型不仅功能强大&#xff0c;而且在各自领…

10.无代码爬虫软件做网页数据抓取流程——工作流程设置与数据预览

首先&#xff0c;多数情况下免费版本的功能&#xff0c;已经可以满足绝大多数采集需求&#xff0c;想了解八爪鱼采集器版本区别的详情&#xff0c;请访问这篇帖子&#xff1a;https://blog.csdn.net/cctv1123/article/details/139581468 八爪鱼采集器免费版和个人版、团队版下…

LLaMA Factory多卡微调的实战教程(持续更新)

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

利用Python语言调用讯飞星火认知大模型接口实战指南

什么是API接口 API&#xff08;应用程序编程接口&#xff09;是一组规则&#xff0c;允许不同的软件系统相互通信。通过API&#xff0c;开发者可以访问外部系统的功能和数据&#xff0c;而无需了解其内部实现。 API接口就像一座桥梁&#xff0c;连接应用程序和服务。例如&…

自动化产线设备联网,协同打造5G智慧工厂

1、需求背景 随着信息技术、物联网、人工智能等领域的飞速发展&#xff0c;智慧工厂成为制造业升级和转型的关键方向。在智慧工厂中&#xff0c;产线设备之间的实时通信和协同操作可以提高整个生产流程的自动化水平。 提升生产效率 通过稳定的网络连接&#xff0c;保证设备之…

Python工具箱系列(五十三)

​​水印 水印是一种常见的图片处理需求。当既需要展示&#xff0c;又需要保护知识产权时&#xff0c;就需要使用文字或者图片来打水印。下面的代码展示了文字水印与图片水印的过程。 ​--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown from pat…

MySQL数据库初识

目录 一.数据库相关概述 1.数据库概念 数据&#xff08;Data&#xff09; 表 数据库&#xff08;database&#xff09; 数据库管理系统&#xff08;DBMS&#xff09; 数据库系统 2.数据库系统发展史 3.数据库分类 3.1.关系数据库 3.2.非关系型数据库 二.MySQL数据库…

vue分页

先看效果 再看代码 <!-- 分页 --><div v-if"pageParams.pageCount > 1" class"flex justify-end mt-6"><n-paginationv-model:page"pageParams.page" v-model:page-size"pageParams.pageSize" :page-count"pa…

【代码随想录】【算法训练营】【第41天】 [416]分割等和子集

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 40&#xff0c;休息&#xff0c;休息一下~ day 41&#xff0c;艰难的周一~ 题目详情 [416] 分割等和子集 题目描述 416 分割等和子集 解题思路 前提&#xff1a;是否可以将数组分为和相等的…

android中的JNI的DEMO

一&#xff1a;源代码 native-lib.cpp #include "native-lib.h"JNIEXPORT jint JNICALL Java_com_example_jnidemo_MainActivity_add(JNIEnv* env, jobject, jint a, jint b) {return a b; }JNIEXPORT jint JNICALL Java_com_example_jnidemo_MainActivity_subtra…

DBeaver连接数据库

1、空白处右键点击 2、创建-连接 3、选择不同的数据库 4、修改信息 (mac)双击&#xff0c;连接&#xff0c;根据自己的需求重命名