【数据结构】排序(下)

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


排序

  • 二、常见排序的实现
    • 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;elsereturn left;}else{if (a[mid] > a[right])return mid;else if (a[left] > a[right])return right;elsereturn 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++];elsetmp[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++];elsetmp[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);//创建一个在堆上的数组作为计数数组,大小为这组数据的范围,将其中的元素全部重置为0for (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)不稳定

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

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

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

相关文章

一个基于大模型的多功能的本地网页语音合成工具

ChatTTS-ui 是一个开源项目&#xff0c;这是一个利用 ChatTTS 技术将文本转换为语音的本地网页界面工具。它不仅支持中英文和数字的混合输入&#xff0c;还提供了丰富的API接口&#xff0c;为开发者和用户提供了极大的便利。 项目地址&#xff1a;https://github.com/jianchang…

1.华为路由器-三层交换机-二层交换机组网连接

AR1配置GE 0/0/0接口IP [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0] [Huawei-GigabitEthernet0/0/0]ip add 1.1.1.1 24 [Huawei]iP route-static 192.168.0.0 16 1.1.1.2三层交换机配置如下 创建vlan [Huawei]vlan batch 10 20配置接口ip [Huawei]int g0/0/1 [Huawei…

数电逻辑门电路分析和Digital仿真

文章目录 1. 逻辑门电路 2. 非门&#xff08;NOT Gate&#xff09; 3. 与门&#xff08;AND Gate&#xff09; 4. 或门&#xff08;OR Gate&#xff09; 5. 与非门&#xff08;NAND Gate&#xff09; 6. 或非门&#xff08;NOR Gate&#xff09; 7. 异或门&#xff08;XO…

开源模型应用落地-LangChain高阶-LCEL-表达式语言(七)

一、前言 尽管现在的大语言模型已经非常强大&#xff0c;可以解决许多问题&#xff0c;但在处理复杂情况时&#xff0c;仍然需要进行多个步骤或整合不同的流程才能达到最终的目标。然而&#xff0c;现在可以利用langchain来使得模型的应用变得更加直接和简单。 LCEL是什么&…

每天五分钟深度学习框架pytorch:多维tensor向量在某一维度的拼接和分割

本文重点 在深度学习中,我们常常需要完成多个向量拼接,同时也要完成向量的分割,在pytorch中已经有封装好的库,我们可以直接调用完成这部分任务。 Cat拼接 c=torch.cat([a,b],dim=0)表示将a和b按0维度进行拼接,需要注意再非dim维度,两个矩阵的维度必须是一致的,不然会拼…

单调栈(续)、由斐波那契数列讲述矩阵快速降幂技巧

在这里先接上一篇文章单调栈&#xff0c;这里还有单调栈的一道题 题目一&#xff08;单调栈续&#xff09; 给定一个数组arr&#xff0c; 返回所有子数组最小值的累加和 就是一个数组&#xff0c;有很多的子数组&#xff0c;每个数组肯定有一个最小值&#xff0c;要把所有子…

Caffe、PyTorch、Scikit-learn、Spark MLlib 和 TensorFlowOnSpark 概述

在 AI 框架方面,有几种工具可用于图像分类、视觉和语音等任务。有些很受欢迎,如 PyTorch 和 Caffe,而另一些则更受限制。以下是四种流行的 AI 工具的亮点。 Caffee Caffee是贾扬青在加州大学伯克利分校(UC Berkeley)时开发的深度学习框架。该工具可用于图像分类、语音和…

胡说八道(24.6.12)——数字电子技术以及Modelsim

上回书说到数电中的最常用的表达式——逻辑表达式(由布尔代数组成)以及常用的两种图表——真值表(真值表表示的是所有的输入可能的线性组合以及输出)和卡诺图(卡诺图则是一种化简工具&#xff0c;排除冗余项&#xff0c;合并可合并项)。 今天&#xff0c;先来看看昨天说的基本逻…

DP:01背包问题

一、背包问题的概述 背包问题是⼀种组合优化的NP完全问题。 本质上是为了找出“带有限制条件的组合最优解” 1、根据物品的个数&#xff0c;分为如下几类&#xff1a; • 01背包问题&#xff1a;每个物品只有⼀个&#xff08;重点掌握&#xff09;• 完全背包问题&#xff1…

ffmpeg封装和解封装介绍-(10)综合完成视频重编码为h265,解封装解码编码再封装

主函数逐句解析&#xff1a; 由于代码太多我们只解析主函数&#xff0c;&#xff08;其他封装函数见前面文章&#xff0c;同时用到了解码编码封装代码&#xff09;。 初始化和参数处理 int main(int argc, char* argv[]) {/// 输入参数处理string useage "124_test_x…

【计算机网络】已解决:“‘ping‘ 不是内部或外部命令,也不是可运行的程序或批处理文件”报错

文章目录 一、问题分析背景二、可能出错的原因三、错误代码示例四、正确解决方法与示例五、注意事项 已解决“‘ping’ 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件”报错 一、问题分析背景 在Windows操作系统中&#xff0c;ping 命令是一个常用的网络诊断…

线程池ThreadPoolExecutor使用指南

线程池ThreadPoolExecutor使用指南 &#x1f9d0;使用线程池的好处是什么&#xff1f; 统一管理&#xff0c;减少资源获取创建的开销&#xff0c;提高利用率。 &#x1f527;线程池的参数 ​ThreadPoolExecutor​ 3 个最重要的参数&#xff1a; ​corePoolSize​ : 任务队列…

docker login 报错: http: server gave HTTP response to HTTPS client

环境&#xff1a; 自建 Harbor、Docker 1. 问题分析 # 命令&#xff0c;这里用的是 IP&#xff0c;可以为域名 docker login -u test 172.16.51.182:31120 # 输入密码 Password:# 报错如下&#xff1a; Error response from daemon: Get "https://172.16.51.182:31120/…

[Algorithm][贪心][增减字符串匹配][分发饼干][最优除法][跳跃游戏Ⅱ][跳跃游戏]详细讲解

目录 1.增减字符串匹配1.题目链接2.算法原理详解3.代码实现 2.分发饼干1.题目链接2.算法原理详解3.代码实现 3.最优除法1.题目链接2.算法原理详解3.代码实现 4.跳跃游戏 II1.题目链接2.算法原理详解3.代码实现 5.跳跃游戏1.题目链接2.算法原理详解3.代码实现 1.增减字符串匹配 …

期末复习6--链表头插法(逆序)尾插法(顺序)---输出链表

头插法 #include <stdio.h> #include <stdlib.h>struct Node //定义结构体 {char data; //数据域struct Node * next; //指针域 };/* 请在这里填写答案 */void PrintList (struct Node * head) {struct Node * s;if(head NULL){printf("None&qu…

Apipost模拟HTTP客户端

模拟HTTP客户端的软件有很多&#xff0c;其中比较著名的就有API-FOX、POSTMAN。 相信很多小伙伴都使用POSTMAN。这篇博客主要介绍Apipost的原因是&#xff0c;Apipost无需下载&#xff0c;具有网页版。 APIFOX的站内下载&#xff1a; Api-Fox&#xff0c;类似于PostMan的软件…

JavaFX 节点

JavaFX Node类javafx.scene.Node是添加到JavaFX 场景图的所有组件 的基类&#xff08;超类&#xff09; 。JavaFX Node 类是抽象的&#xff0c;因此你只需将 Node 类的子类添加到场景图中。场景图中的所有 JavaFX Node 实例共享一组由 JavaFX Node 类定义的公共属性。本 JavaFX…

一文了解Redis

一.什么是Redis 与MySQL一样&#xff0c;Redis也是客户端服务器结构的程序&#xff0c;是基于内存的键值对存储系统&#xff0c;属于NoSQL的一种。与很多键值对数据库不同的是&#xff0c;Redis 中的值可以是由 string&#xff08;字符串&#xff09;、hash&#xff08;哈希&a…

【算法专题--链表】删除排序链表中的重复元素II -- 高频面试题(图文详解,小白一看就懂!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐ 双指针 -- 采用 哨兵位头节点 &#x1f95d; 什么是哨兵位头节点&#xff1f; &#x1f34d; 解题思路 &#x1f34d; 案例图解 四、总结与提炼 五、共勉 一、前言 删除排序链表中的重复元素II元素这道题&#xff0c…

【JKI SMO】框架讲解(二)

JKI State Machine 讲解 将JKI State Machine 模板拖曳到程序框图中&#xff0c; 如下图&#xff0c; 此模板会默认放置一个OK按钮在前面板中&#xff0c;用于提示用户如何增加一个简单的用户事件去使用此框架。 “Event Structure”&#xff0c;Idle&#xff1a;此分支可以设…