【数据结构】一文了解七大排序算法

文章目录

  • 前言
  • 一.直接插入排序
    • 插入排序思想
    • 插入排序代码实现
    • 插入排序总结
  • 二.希尔排序
    • 希尔排序思想
    • 希尔排序代码实现
    • 希尔排序总结
  • 三.选择排序
    • 选择排序思想
    • 选择排序代码实现
    • 选择排序总结
  • 四.堆排序
    • 堆排序思想
    • 堆排序代码实现
    • 堆排序总结
  • 五、冒泡排序
    • 冒泡排序思想
    • 冒泡排序代码实现
    • 冒泡排序总结
  • 六、快速排序
    • 快速排序思想
    • 快速排序代码实现
    • 快速排序总结
  • 七.归并排序
    • 归并排序思想
    • 归并排序代码实现
    • 归并排序总结
  • 总结


在这里插入图片描述

前言

所谓排序算法,即通过特定的算法因式将一组或多组数据按照既定模式进行重新排序。这种新序列遵循着一定的规则,体现出一定的规律,因此,经处理后的数据便于筛选和计算,大大提高了计算效率。对于排序,我们首先要求其具有一定的稳定性,即当两个相同的元素同时出现于某个序列之中,则经过一定的排序算法之后,两者在排序前后的相对位置不发生变化。换言之,即便是两个完全相同的元素,它们在排序过程中也是各有区别的。今天,我们介绍一下常见的排序算法。
在这里插入图片描述


一.直接插入排序

在这里插入图片描述

插入排序思想

插入排序算法是基于某序列已经有序排列的情况下,通过一次插入一个元素的方式按照原有排序方式增加元素。这种比较是从该有序序列的最末端开始执行,即要插入序列中的元素最先和有序序列中最大的元素比较,若其大于该最大元素,则可直接插入最大元素的后面即可,否则再向前一位比较查找直至找到应该插入的位置为止。插入排序的基本思想是,每次将1个待排序的记录按其关键字大小插入到前面已经排好序的子序列中,寻找最适当的位置,直至全部记录插入完毕。执行过程中,若遇到和插入元素相等的位置,则将要插人的元素放在该相等元素的后面,因此插入该元素后并未改变原序列的前后顺序。我们认为插入排序也是一种稳定的排序方法。

插入排序代码实现

void InsertSort(int* a, int n)
{for (int i = 0; i < n-1; i++){int end = i;int tmp = a[end+1];while (end >= 0){if (a[end] > tmp){a[end + 1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}

插入排序总结

直接插入排序的特性总结:

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1),它是一种稳定的排序算法
  4. 稳定性:稳定

二.希尔排序

在这里插入图片描述

希尔排序思想

希尔排序的思想实际上是将数列一次又一次的预排序,让数组趋近于有序,之后在进行插入排序得到答案。预排序就是先将数组中的数进行分组,将相隔gap距离的树分为一组,在一组中进行插入排序使之有序,再将gap逐渐缩小,最后gap等于1时,就是插入排序。通过分组的方法可以让后面的数更快的来到前面,让前面的数更快的来到后面。但是gap越大,越不接近有序,gap太小效率提高就不明显,所以经过前人的多次实验得到,当gap处于gap=gap/3+1动态变化中时效率较高。

希尔排序代码实现

void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 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 = end - gap;}else{break;}}a[end + gap] = tmp;}}}

希尔排序总结

希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就
    会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,一般认为是O(n^1.5)
  4. 稳定性:不稳定

三.选择排序

在这里插入图片描述

选择排序思想

选择排序的思想比较简单,首先我们需要遍历一遍数组,找到最小的值,再将最先的值与第一个值交换,这样就排好了第一个数。以此类推,遍历后面的数组,再找最小的数,与前面交换,最后有序。我们也可以升级一下,遍历一次数组,找到最大最小的两个数,最小的放前面,最大的放后面。但是要注意当你交换完最小的数后,最大的那个数可能位置会变化,要单独讨论。

选择排序代码实现

void SelectSort(int* a, int n)
{int right = n - 1;int left = 0;for (right = n - 1, left = 0; right > left; left++, right--){int maxi = right;int mini = left;for (int i = left; i <= right; i++){if (a[mini] > a[i]){mini = i;}if (a[maxi] < a[i]){maxi = i;}}Swap(&a[left], &a[mini]);if (maxi == left){maxi = mini;}Swap(&a[maxi], &a[right]);}
}

选择排序总结

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

四.堆排序

在这里插入图片描述

堆排序思想

堆排序首先就是建堆,我们通过从倒数第二层开始向下调整建堆。如果排升序就建大堆,排降序建小堆。以升序为例,我们通过建堆在根节点得到最大的数,再将最大的数与最后的数交换位置,最大的数就排好了,再通过建堆找第二大的数,最后有序。

堆排序代码实现

void AdjustDown(HPDataType* a, int n, int parent)
{int child = 2 * parent + 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)
{int i = 0;for (i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, n, i);}int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);end--;AdjustDown(a, end+1, 0);}}

堆排序总结

堆排序的特性总结:

  1. 堆排序使用堆来选数,效率就高了很多。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

五、冒泡排序

在这里插入图片描述

冒泡排序思想

冒泡排序算法是把较小的元素往前调或者把较大的元素往后调。这种方法主要是通过对相邻两个元素进行大小的比较,根据比较结果和算法规则对该二元素的位置进行交换,这样逐个依次进行比较和交换,就能达到排序目的。冒泡排序的基本思想是,首先将第1个和第2个记录的关键字比较大小,如果是逆序的,就将这两个记录进行交换,再对第2个和第3个记录的关键字进行比较,依次类推,重复进行上述计算,直至完成第(n一1)个和第n个记录的关键字之间的比较,此后,再按照上述过程进行第2次、第3次排序,直至整个序列有序为止。排序过程中要特别注意的是,当相邻两个元素大小一致时,这一步操作就不需要交换位置,因此也说明冒泡排序是一种严格的稳定排序算法,它不改变序列中相同元素之间的相对位置关系。

冒泡排序代码实现

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

冒泡排序总结

冒泡排序的特性总结:

  1. 冒泡排序是一种非常容易理解的排序
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定

六、快速排序

在这里插入图片描述

快速排序思想

快速排序采用的是分治思想,即在一个无序的序列中选取一个任意的基准元素pivot,利用pivot将待排序的序列分成两部分,前面部分元素均小于或等于基准元素,后面部分均大于或等于基准元素,然后采用递归的方法分别对前后两部分重复上述操作,直到将无序序列排列成有序序列。

快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
1、首先设定一个分界值,通过该分界值将数组分成左右两部分。
2、将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。
3、然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
4、重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

快速排序代码实现

void QiuckSort(int* a, int left, int right)
{if (left >= right){return;}int keyi = left;int begin = left, end = right;while (end > begin){while(end > begin && a[end] >= a[keyi]){end--;}while (end > begin && a[begin] <= a[keyi]){begin++;}Swap(&a[begin], &a[end]);}Swap(&a[begin], &a[keyi]);QiuckSort(a, left, begin - 1);QiuckSort(a, begin + 1, right);
}

快速排序总结

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(logN)
  4. 稳定性:不稳定

七.归并排序

在这里插入图片描述

归并排序思想

归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾

归并排序代码实现

void _MergeSort(int* a, int* tmp, int begin, int end)
{if (begin >= end){return;}int mid = (begin + end) / 2;_MergeSort(a, tmp, begin, mid);_MergeSort(a, tmp, mid + 1, end);int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;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+begin, tmp+begin, (end - begin + 1) * sizeof(int));
}
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc");return;}_MergeSort(a, tmp, 0, n - 1);free(tmp);tmp == NULL;
}

归并排序总结

归并排序的特性总结:

  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定

总结

以上就是今天要讲的内容,本文仅仅简单介绍了几种常见的排序算法,实际上还有计数排序,桶排序等许多排序算法。如果喜欢这篇文章,期待你的一键三连。

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

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

相关文章

Dify 与 Xinference 最佳组合 GPU 环境部署全流程

背景介绍 在前一篇文章 RAG 项目对比 之后&#xff0c;确定 Dify 目前最合适的 RAG 框架。本次就尝试在本地 GPU 设备上部署 Dify 服务。 Dify 是将模型的加载独立出去的&#xff0c;因此需要选择合适的模型加载框架。调研一番之后选择了 Xinference&#xff0c;理由如下&…

易我分区大师18.8.0更新:两大功能改进

近日&#xff0c;易我分区大师18.8.0更新上线。此次更新重点改进了系统克隆功能&#xff0c;支持从第二块系统盘&#xff08;从盘&#xff09;克隆系统&#xff1b;同时&#xff0c;软件支持将分区的文件系统格式从FAT转换成exFAT。 01、系统克隆 系统克隆功能旨在帮助用户在…

pinia学习

conuter.ts <template><div><!-- 显示当前的计数 --><p>Count: {{ count }}</<!-- 显示计算的双倍计数 --><p>Double Count: {{ doubleCount }}</p><!-- 点击按钮以增加计数 --><button click"increment">…

基于红黑树对map和set的封装

前言 前面我们已经对红黑树做了介绍和实现&#xff0c;本期我们来对红黑树进一步改造&#xff0c;然后基于改造后的红黑树封装出map和set&#xff01; 本期内容介绍 • 红黑树的改造 • 红黑树的迭代器实现 • map的封装 • set的封装 • 全部源码 ● 红黑树的改造 我们目前…

未来互联网的新篇章:深度解析Facebook的技术与战略

随着科技的飞速发展和社会的不断变迁&#xff0c;互联网作为全球信息交流的重要平台&#xff0c;正经历着前所未有的变革和演进。作为全球最大的社交媒体平台之一&#xff0c;Facebook不仅是人们沟通、分享和互动的重要场所&#xff0c;更是科技创新和数字化进程的推动者。本文…

音视频开发—FFmpeg 从MP4文件中抽取视频H264数据

文章目录 MP4文件存放H264数据方式MP4 文件结构概述H.264 数据在 MP4 中的存储1. ftyp 盒子2. moov 盒子3. mdat 盒子 H.264 数据在 stsd 盒子中的存储&#xff08;AVC1&#xff09;AVC1与Annex-B 格式&#xff08;裸 H.264 流&#xff09;的区别 从MP4文件中提取H264裸流步骤&…

java使用easypoi模版导出word详细步骤

文章目录 第一步、引入pom依赖第二步、新建导出工具类WordUtil第三步、创建模版word4.编写接口代码5.导出结果示例 第一步、引入pom依赖 <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><…

怎么压缩视频?推荐7款必备视频压缩软件免费版(强烈建议收藏)

如今&#xff0c;视频内容日益丰富&#xff0c;并占据了许多人的日常娱乐和工作生活。然而&#xff0c;随着高清和超高清视频的普及&#xff0c;视频文件的体积也越来越大&#xff0c;给存储和传输带来了挑战。因此&#xff0c;学会如何压缩视频文件成为了许多人的需求之一。本…

关于锂电池的充电过程

锂电池的充电阶段大概可以分为四个阶段&#xff1a;涓流充电、恒流充电、恒压充电以及充电终止。 涓流充电&#xff1a;这是充电过程的第一阶段&#xff0c;主要用于对完全放电的电池单元进行预充&#xff08;恢复性充电&#xff09;。当电池电压低于大概3V时&#xff0c;采用最…

【学习css1】flex布局-页面footer部分保持在网页底部

中间内容高度不够屏幕高度撑不开的页面时候&#xff0c;页面footer部分都能保持在网页页脚&#xff08;最底部&#xff09;的方法 1、首先上图看显示效果 2、奉上源码 2.1、html部分 <body><header>头部</header><main>主区域</main><foot…

PaintsUndo - 一张照片一键生成绘画过程视频 本地一键整合包下载

这就是ControlNet作者张吕敏大佬的新作&#xff0c;PaintsUndo。只要你有一张图片&#xff0c;PaintsUndo 就能让它变成完整的绘画过程视频。这科技&#xff0c;绝了。 你有没有想过&#xff0c;一张静态图片也能变成一个绘画教程? PaintsUndo 就是这么神奇。你只需要提供一…

通过手机供网、可修改WIFI_MAC的网络设备

一、修改WIFI mac&#xff08;bssid&#xff09; 取一根网线&#xff0c;一头连着设备黄色网口、一头连着电脑按住设备reset按键&#xff0c;插入电源线&#xff0c;观察到蓝灯闪烁后再松开reset按键 打开电脑浏览器&#xff0c;进入192.168.1.1&#xff0c;选择“MAC 地址修改…

【Spring Boot】Spring原理:Bean的作用域和生命周期

目录 Spring原理一. 知识回顾1.1 回顾Spring IOC1.2 回顾Spring DI1.3 回顾如何获取对象 二. Bean的作用域三. Bean的生命周期 Spring原理 一. 知识回顾 在之前IOC/DI的学习中我们也用到了Bean对象&#xff0c;现在先来回顾一下IOC/DI的知识吧&#xff01; 首先Spring IOC&am…

可视化学习:如何用WebGL绘制3D物体

在之前的文章中&#xff0c;我们使用WebGL绘制了很多二维的图形和图像&#xff0c;在学习2D绘图的时候&#xff0c;我们提过很多次关于GPU的高效渲染&#xff0c;但是2D图形的绘制只展示了WebGL部分的能力&#xff0c;WebGL更强大的地方在于&#xff0c;它可以绘制各种3D图形&a…

C语言之数据在内存中的存储(2),浮点数在内存中的存储

目录 前言 一、引例 二、浮点型在内存中的存储 三、浮点数在内存中的存和取过程 1.浮点数的存储过程 2.浮点数的取过程 四、引例解析 总结 前言 想知道浮点数在内存中是如何存储的吗&#xff0c;本文就告诉你答案&#xff0c;虽然一般情况题目还是面试涉及到浮点数在内…

新华三H3CNE网络工程师认证—ACL使用场景

ACL主要用于实现流量的过滤&#xff0c;业务中网络的需求不止局限于能够连同。 一、过略工具 你的公司当中有研发部门&#xff0c;包括有财务部门&#xff0c;财务部门的访问是要做到控制的&#xff0c;防止被攻击。 这种的过滤方法为&#xff0c;在设备侧可以基于访问需求来…

AI算力发展现状与趋势分析

综合算力发展现状与趋势分析 在数字经济的疾速推动下&#xff0c;综合算力作为驱动各类应用和服务的新型生产力&#xff0c;其价值日益凸显。我们深入探讨了综合算力的定义、重要性以及当前发展状况&#xff1b;并从算力形态、运力性能和存储技术等角度&#xff0c;预见了其发展…

基于Java技术的校友社交系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果你对校友社交系统感兴趣或者有相关需求&#xff0c;可以私信联系我。 开发语言 Java 数据库 MySQL 技术 Java技术SpringBoot框架 工具 IDEA/Eclipse、Navicat、Maven 系统展示 首页 校友会信息界面 校友活动…

Sqli-labs 3

1.按照路径http://localhost/sqli-labs/sqli-labs-master/Less-3/进入 2.判断注入类型----字符型 Payload&#xff1a;?id1’) and 11-- 注&#xff1a;根据报错提示的语法错误&#xff0c;在第一行中使用接近’union select 1,2,3--’)的正确语法 3.判断注入点&#xff1a;…

【Linux】vim详解

1.什么是vi/vim? 简单来说&#xff0c;vi是老式的文本编辑器&#xff0c;不过功能已经很齐全了&#xff0c;但是还是有可以进步的地方。vim则可以说是程序开发者的一项很好用的工具&#xff0c;就连 vim的官方网站&#xff08; http://www.vim.org&#xff09;自己也说vim是一…