一篇文章弄懂数据结构中的各种排序_插入排序_冒泡排序_快速排序_堆排序_归并排序_基数排序

news2024/9/29 0:10:27

文章目录

  • 一篇文章弄懂数据结构中的各种排序
  • 1.排序的概念
  • 2. 插入排序
    • 2.1 直接插入排序
    • 2.2 折半插入排序
    • 2.3 希尔排序
  • 3.冒泡排序
    • 3.1 算法原理
    • 3.2 性能分析
  • 4.快速排序
    • 4.1 算法原理
    • 4.2 性能分析
  • 5. 选择排序
    • 5.1 简单选择排序
    • 5.2 堆排序
      • 5.1 算法流程
      • 5.2 算法效率分析
      • 5.3 堆排序的插入和删除操作(了解)
        • 5.3.1 堆排序的插入操作
        • 5.3.2 堆排序的删除操作
      • 5.4 代码实现堆排序
  • 6. 归并排序
    • 6.1 算法思想和算法流程
    • 6.2 算法效率分析
  • 7. 基数排序
    • 7.1 算法思想和算法流程
    • 7.2 算法效率分析
  • 8.例题
    • 堆排序
    • 快速排序
    • 希尔排序

一篇文章弄懂数据结构中的各种排序

1.排序的概念

排序:将各元素按关键字递增或递减顺序重新排列
评价指标:
稳定性:关键字相同的元素经过排序后相对顺序是否会发生改变。
注意:稳定性跟算法的优劣无关
时间复杂度,空间复杂度

分类:
内部排序:数据都在内存中
外部排序:数据太多,无法全部存入内存。

2. 插入排序

2.1 直接插入排序

算法思想:
从头开始,每次将一个待排序的记录,按照关键字大小加入到前面已经排好的子序列中。

流程是:
一个待排序的记录,从已经排好的子序列的尾部开始比较,找到小于或等于它的值,将其插入进去,得到新的子序列。

空间复杂度: O(1)
时间复杂度
最好时间复杂度(全部有序):O(n)
最坏时间复杂度(全部逆序):O(n2)
平均时间复杂度:O(n2)

算法稳定性:稳定

2.2 折半插入排序

前面说的插入排序,是通过顺序的方式查找需要插入的位置,由于子序列已经是顺序的,可以优化成通过折半(二分)查找插入位置。

比起“直接插入排序”,比较关键字的次数减少了,但是移动元素的次数没变,整体来看时间复杂度依然是o(n2)

对链表进行插入排序
虽然对链表进入插入排序移动的次数变少了,但是关键字对比的次数依然是O(n2)树数量级,时间复杂度依然是O(n2)

2.3 希尔排序

引入希尔排序:

我们不难发现插入排序最适合用于已经有序的序列,或者部分有序有序的序列。
希尔排序就是让部分有序-直接插入-部分有序–直接插入 的过程

算法思想:
先将待排序表分割成若干特殊子表,对每个子表进行直接插入的过程。

如何划分?
每一次划分设置一个增量d,如d=4,就是把相隔距离=4的元素,化为一组,如 1,5,9…,为一组2,6,10,为一组,以此类推,希尔建议每次设置增量后,下一次取增量取上一次增量的一半,但是在实际的解题中,要看具体要求,具体情况具体分析。

在这里插入图片描述

性能分析:
空间复杂度:o(1)
时间复杂度:无法计算,但优于直接插入排序
稳定性:不稳定
适用性:仅适用于顺序表,不适用于链表

3.冒泡排序

3.1 算法原理

算法原理:
每次从后往前后或从前往后,两两一组,逐次比较,如果后一个更小(从后往前),则交换它们,注意,整个过程中,元素只交换,容纳两个元素的窗口移动。

每一趟比较都可以使一个元素移动到最终位置,已经确定最终位置的元素在之后的比较中无需再对比。

如果某一趟排序过程中未发生交换,则算法可提前结束。

代码思路:
两层循环,最外层保证,每一次循环让一个元素移动到最后一个位置,故n-1次就能保证n-1个元素都在最后的位置上,故n个元素都在最后的位置上。

for(int i=0;i<n-1;i++)

内层每次和最后一个元素j比较,若最后一个元素小,则和j-1交换,逐步往前遍历,小就交换,不小就遍历,直到到达最终位置。

for(int j=n-1;j>i;j--)
{
	if(a[j]<a[j-1])
	{
		swap(a[j],a[j-1]); //伪代码
	}
}

优化是定一个布尔变量flag,若一次循环并未改变flag的值,则直接退出循环

给出一个完整的c语言冒泡排序的算法:

 for(int i=0;i<numsSize-1;i++) 
    {
        for(int j=0;j<numsSize-1-i;j++)
        {
            if(nums[j]>nums[j+1])
            {
                int temp=0;
                temp=nums[j+1];
                nums[j+1]=nums[j];
                nums[j]=temp;
            }
        }
    }

3.2 性能分析

空间复杂度:o(1)

时间复杂度:
最好:o(n)
最差:o(n2)
平均:o(n2)

稳定性:稳定

适用性:顺序表,链表都可以。

4.快速排序

4.1 算法原理

首先将最左边待排元素移出数组,最左边设置low指针,最右边设置high指针,两边交替使用,从high指针开始移动,判断当前high指针所指向元素,是否大于待排元素,如果小于,则继续向左移动,继续比较,如果小于,则将该元素,移入到low指针指向的空间中,然后开始移动low指针,进行low指针的判断,此时low与high指针的职能交换。然后彼此互换。

快速排序代码如下:

int partition(int a[],int low,int high)
{
    int pivot=a[low];
    
    while (low<high) {
        while(a[high]>=pivot&&low<high)
        {
            high--;
        }  //出循环之后,要移动high指向的元素赋值给low中空下来的位置
        a[low]=a[high];
        while(a[low]<=pivot&&low<high)
        {
            low++;
        }
        a[high]=a[low];
    }
    a[low]=pivot;
    return low;  //此时low=high
}


void quicksort(int a[],int low,int high)
{
    if(low<high)
    {
        int pivot=partition(a, low, high);
        quicksort(a, low, pivot-1);
        quicksort(a, pivot+1, high);
    }
}

4.2 性能分析

空间复杂度:

  • 最好:O(n)
  • 最坏:O(logn)

空间复杂度:递归工作栈,确定空间复杂度

时间复杂度:
最好:O(n2)
最坏:O(n logn)
平均:O(n long)

稳定性:不稳定

5. 选择排序

5.1 简单选择排序

每一趟在待排序元素选取关键字最小的元素加入有序子序列。

算法性能分析
空间复杂度:o(1)

时间复杂度:o(n2)
总共需要对比关键字:n(n-1)/2

稳定性:不稳定

适用性:既可以用于顺序表,也可用于链表

5.2 堆排序

什么是堆?
大根堆,意味着根最大,根结点大于它的左右结点,同理,每一个结点都满足这个性质,我们就把这个树形结构叫做大根堆。
小根堆反之。

5.1 算法流程

1️⃣建立大根堆

把所有非终端结点都检查一遍,是否满足大根堆的要求,如果不满足,则从后往前进行调整。
检查当前结点是否满足根>=左右,若不满足,则当前结点与更大的一个孩子互换
若元素互换破坏了下一级的堆,则采用相同的方法继续往下调整(小元素不断“下坠”)

2️⃣取根结点加入有序子序列(结果),并将待排序序列中的最后一个元素交换(肯定是当前的一个叶子结点)。然后回到1️⃣

5.2 算法效率分析

一个结点,每下坠一层,最多只需比较关键字2次。
若树高为h,某结点在第i层,则将这个结点向下调整最多只需要下坠h-i层,关键字对比次数不超过2(h-i)

建堆的过程,关键字对比次数不超过4n,建堆时间复杂度=o(n).
2️⃣的过程,时间复杂度是O(nlog2n)
总的时间复杂度=O(nlogn)

稳定性:不稳定
基于大根堆的堆排序得到递增序列,基于小根堆得到递减序列。

5.3 堆排序的插入和删除操作(了解)

5.3.1 堆排序的插入操作

新元素放到表尾(堆底)
根据大/小根堆堆要求,新元素不断上升,直到无法继续上升为止。

每次上升调整只需对比关键字1次

5.3.2 堆排序的删除操作

被删除元素用表尾(堆底)元素代替
根据大/小根堆堆要求,替代元素不断下坠,直到无法继续下坠为止。

每次下坠调整可能需要对比关键字2次,也可能只需对比1次。

5.4 代码实现堆排序

void swap(int *a, int *b)
{
    int temp = *a;
    *a=*b;
    *b=temp;
}


void heapify(int a[],int length,int i)
{
    //找到当前结点和它的左右孩子孩子中最大的一个,将最大的结点与当前结点交换,若当前结点就是最大的,则不交换
    int largest=i;
    int lson=i*2+1;
    int rson=i*2+2;
    
    if(lson<length&&a[largest]<a[lson]) largest=lson;         //左右孩子不能超出这个树的范围<l
    if(rson<length&&a[largest]<a[rson]) largest=rson;
    
    if(largest!=i)
    {
        swap(&a[i], &a[largest]);
        heapify(a,length,largest);
    }
}

void heap_sort(int a[],int length)
{
    //堆排序的大体过程,首先建立堆,然后将当前的根节点和最大下标的叶子结点,交换,然后将新的叶子结点删除,并且重新调整堆
    // 第一步,建立堆,遍历全部的非叶子结点,从下往上的遍历,调整,确保一遍完成建立堆堆操作
    //因为是从0开始存储,对应的父结点下标是length/2-1,假如从1开始存储,就是length/2
    for(int i=length/2-1;i>=0;i--) //从最后一个非叶子结点开始,遍历全部的非叶子结点,此时的就能找到最大的值放在根上
    {
        heapify(a, length, i);
    }
    //第二步开始,多次调整堆,每一次都会确定一个位置,即将当前的根与最后一个叶子结点交换,再重新进行建堆操作
    for(int i=length-1;i>0;i--)
    {
        swap(&a[0], &a[i]); //a[0]是最大的那个,a[i]是当前要确定的位置
        heapify(a, i, 0); //当前的长度就是i,当前要从根开始调整;
    }
    
}

在这里插入图片描述

6. 归并排序

6.1 算法思想和算法流程

算法思想:
把两个或多个已经有序的序列合并成一个序列,合并的过程,就是两个有序序列依次对比把更小(或更大的拿出来合并)。

算法流程:
以二路归并为例
在这里插入图片描述

从每一个元素都是一个队列开始都是一个独立的有序序列,相邻两个元素合并成一个,在排好序,以此类推,不断将相邻的两个有序序列合并并排好序,直到就剩一个有序序列为止。

6.2 算法效率分析

算法的空间复杂度:O(n)
算法的时间复杂度:O(nlogn)
稳定性:稳定的

7. 基数排序

7.1 算法思想和算法流程

算法思想:
不比较关键字,而是按位数比较,比如给多个三位数排序,就先比较它们的个位数,再比较它们的十位数,最后比较它们的百位数。

算法流程:
以多个三位数的比较为例
在这里插入图片描述

首先设置十个队列,因为0-9是10个数

从左往右,根据个位数的,将相应的个位数加入队列中,然后再从左往右,把各个队列拿出来拼到一起
在这里插入图片描述

上面个位数就排好列,再排十位数,如法炮制,最后排百位数

7.2 算法效率分析

n是元素个数,d是趟数,比如三位数就是三趟,r是辅助队列的个数,比如0-9就是10个

空间复杂度:o(r)
时间复杂度:o(d(n+r))
稳定性:稳定
擅长处理什么样的问题?
n大,但是r和d小的问题

8.例题

堆排序

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

快速排序

真题1:
在这里插入图片描述
在这里插入图片描述
真题2:
在这里插入图片描述

在这里插入图片描述

希尔排序

希尔排序中,值得注意的点是,一个数据可能被多次的比较,比如d=5,第一个和第六个元素比较,等到了第6个元素,第6个元素再与第11个元素比较(如果存在第11个元素的话)

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

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

相关文章

2024CSCO 芦康沙妥珠单抗创造晚期TNBC二线治疗新高度

前言 “魔法子弹”的概念从上世纪初提出&#xff0c;经过一百多年的不断探索&#xff0c;抗体药物偶联物&#xff08;ADC&#xff09;从理想照进现实&#xff0c;达到今天百舸争流的盛况&#xff0c;被认为是极具前景的创新疗法&#xff0c;全球范围内已有十余款产品被批准用于…

使用Mendeley生成APA格式参考文献

Mendeley 是一款文献管理工具&#xff0c;可以在Word中方便的插入引用文献。 效果对比&#xff1a; 注&#xff1a;小绿鲸有三种导出格式&#xff0c;分别为复制、导出为Bibtex和导出为Endnote三种。 Mendeley 下载与安装 Download Mendeley Reference Manager For Desktop m…

报道|解读INFORMS期刊影响因子的下降及运筹与管理科学出版的未来

编者按 David Simchi-Levi和Tinglong Dai老师近期在ORMS Today上发表了一篇名为拥抱影响力的变化&#xff1a;解读INFORMS期刊影响因子的下降及运筹与管理科学出版的未来的文章&#xff0c;探讨了近几年INFORMS的大多数期刊影响因子下降的原因以及带给我们的启示。 2023年7月&a…

Qt 首次配置 Qt Creator 14.01 for Python

前言&#xff1a; 如何用QT实现Python的配置的交互界面。本文从0开始&#xff0c;进行实践的介绍。 在上一节里面&#xff0c;我们做了社区版本的配置&#xff1a; https://blog.csdn.net/yellow_hill/article/details/142597007?spm1001.2014.3001.5501 这一节&#xff0…

Linux —— udp实现群聊代码

一、介绍 前面我们一步步模拟实现了一个简单的udp服务器和客户端&#xff0c;通过这个服务器&#xff0c;我们简单实现一个群聊的功能&#xff0c;本篇是专门用来记录代码的&#xff0c;详细的实现思路可以去参考我其他两篇&#xff0c;Socket编程&#xff08;一&#xff09;和…

Android性能优化相关的10个经典面试题

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 以下是一些Android性能优化面试问题&#xff0c;包括问题和参考解答&#xff1a; 1. 如何优化Android应用的启动速度&#xff1f; 答案&#…

零基础教你如何开发webman应用插件

0X07 发布插件应用 插件应用发布地址 https://www.workerman.net/app/create。填写好发布相关信息 0X08 上传源码zip文件 提交完成之后等待官方审核就可以啦&#xff01; 0X09 安装插件 应用插件安装有两种方式 在插件市场安装 进入官方管理后台webman-admin 的应用插件页点击…

tauri开发配置文件和文件夹访问路径问题

文件夹没权限&#xff1a;Unhandled Promise Rejection: path not allowed on the configured scope: /Users/song/Library/Application Support/com.pakeplus.app/assets/default.png 没有文件夹&#xff0c;需要先创建&#xff1a;Unhandled Promise Rejection: path: /Users…

GB28181信令交互流程及Android端设备对接探讨

GB28181规范必要性 好多开发者在做比如执法记录仪、智能安全帽、智能监控等设备端视频回传技术方案选型的时候&#xff0c;不清楚到底是用RTSP、RTMP还是GB28181&#xff0c;对GB28181相对比较陌生&#xff0c;我们就GB28181规范的必要性&#xff0c;做个探讨&#xff1a; 实现…

vue+UEditor附件上传问题

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

【软件测试】详解软件测试中的测试级别

目录 一、测试级别二、组件测试三、开发者测试3.1测试与调试3.2 组件测试目标3.3 测试功能 四、稳健性测试4.1 效率的测试4.2 测试可维护性4.3 测试策略4.4 白盒测试 一、测试级别 软件系统通常是由许多子系统组成的&#xff0c;而这些子系统又是由多个组件组成的&#xff0c;…

基于STM32的无人驾驶车辆系统

目录 引言项目背景环境准备 硬件准备软件安装与配置系统设计 系统架构关键技术代码示例 传感器数据采集与处理路径规划与避障控制实时反馈与控制系统应用场景结论 1. 引言 随着无人驾驶技术的发展&#xff0c;嵌入式系统在无人驾驶车辆中的应用变得越来越重要。STM32作为高效…

ECMAScript 与 JavaScript 的区别详解

ECMAScript 与 JavaScript 的区别详解 在前端开发的学习过程中&#xff0c;很多开发者会遇到两个常见的术语&#xff1a;ECMAScript 和 JavaScript。这两个术语常常被混淆&#xff0c;因为它们密切相关&#xff0c;甚至有时被认为是同一件事。本文将详细解析 ECMAScript 和 Ja…

盘点4款专业高效的数据恢复工具。

超级兔子数据恢复工具具有广泛的系统适配性&#xff0c;功能丰富&#xff0c;操作简单&#xff0c;是一款比较专业的数据恢复软件。如果大家在为数据丢失而烦恼的话&#xff0c;我可以推荐几款好用的数据恢复软件给大家。 1、福昕数据高效恢复 直通车&#xff1a;http://www.p…

有哪些使用的电脑安全小技巧?

以下是一些电脑使用的安全技巧&#xff1a; 1. 定期更新系统和软件&#xff1a; 操作步骤&#xff1a;打开系统设置中的“更新和安全”选项&#xff0c;启用自动更新。对于软件&#xff0c;在其设置中查找更新选项并定期检查。 2. 设置强密码&#xff1a; 操作步骤&#xf…

yakit使用教程(二,配置证书并进行抓包改包操作)

前文链接&#xff1a;yakit下载安装教程。 一&#xff0c;下载并配置证书。 点击mitm&#xff0c;在跳转后的页面点击高级配置。 点击证书下载。 点击下载到本地并打开&#xff08;建议下载到桌面&#xff09;。 在火狐浏览器下载并安装FoxyProxy&#xff0c;具体参数配置如上…

TIM--定时器

TIM–基本定时器 大纲 定时器分类时基基本定时器高级控制定时器高级控制定时器功能框图输入捕获应用输出比较应用定时器初始化结构体详解 具体案例 定时器分类 STM32F1 系列中&#xff0c;除了互联型的产品&#xff0c;共有 8 个定时器&#xff0c;分为基本定时器&#xf…

深入探讨在线教育系统源码:搭建知识付费平台实战方案详解

知识付费平台是软件开发行业内炙手可热的项目&#xff0c;其受众群体非常广&#xff0c;也是很多小伙伴提问比较多的&#xff0c;今天小编将从在线教育系统源码开始&#xff0c;为大家讲解一个知识付费平台的搭建开发实战方案。 一、系统架构设计 搭建在线教育系统需考虑以下几…

linux没有权限安装zip应该如何解压压缩包

linux没有权限安装zip应该如何解压压缩包 &#xff08;1&#xff09;尝试使用unzip命令直接解压 &#xff08;2&#xff09;发现没有安装先安装&#xff0c;发现没有权限安装 &#xff08;3&#xff09;再试试tar命令&#xff0c;好像安装了&#xff0c;但是不能用&#x…

罕见 P0 故障!上交所崩了 ~

大家好啊&#xff0c;我是董董灿。 昨天&#xff08;9月27号&#xff09;很多朋友可能都刷到一个消息&#xff1a;上交所崩了。 原因是在近期经济政策的刺激下&#xff0c;我大A股市场出现反弹&#xff0c;很多投资者纷纷涌入大A进行交易。 A 股反弹本来是件好事&#xff0c…