常见排序算法

前言:

排序算法可以说是每一个程序员在学习数据结构和算法时必须要掌握的知识点,同样也是面试过程中可能会遇到的问题,在早些年甚至还会考冒泡排序。由此可见呢,掌握一些常见的排序算法是一个程序员的基本素养。虽然现在的语言标准库里都有直接的排序函数,但是作为一个学习者,我们应当抱着“知其然,还要知其所以然”的态度去学习。

1.常见的排序算法有哪些?

常见的排序算法及其性能:

算法名称平均时间复杂度稳定性
直接插入排序N^2稳定
希尔排序N^1.25--1.6N^1.25不稳定
选择排序N^2不稳定
堆排序NlogN不稳定
冒泡排序N^2稳定
快速排序NlogN不稳定
归并排序NlogN稳定

这里呢我只讲一些效率比较高的排序算法,比如快排、并排、堆排序、希尔排序。

2.常见排序算法的实现

2.1希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成grap个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序(插入排序)。然后,重复上述分组和排序的工作。当分组间距为1时,所有记录在统一组内排好序。

 

代码实现:


// 希尔排序
void ShellSort(int* a, int n) {int gap = n - 1;//grap为分组之间的间隔while (gap > 1) {gap = gap / 3 + 1;//每次分组的间距都越来越小,直到间距为一for (int i = 0; i < gap; i++) {//i表示每组的开头位置for (int j = i; j < n - gap; j += gap) {//对每一组插入排序int end = j;int temp = a[j + gap];while (end >= 0) {if (temp < a[end]) {a[end + gap] = a[end];end -= gap;}else {break;}}a[end + gap] = temp;}}}
}

希尔排序的特性总结:

1. 希尔排序是对直接插入排序的优化

2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时(相当于对整个数组进行插入排序),数组已经接近有序的了,这样就 会很快。这样整体而言,可以达到优化的效果。

3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好多书中给出的 希尔排序的时间复杂度都不固定,目前也只能给出一个大概的复杂度。

2.2堆排序

关于堆排序在之前的博客中已经详细讲解过,有兴趣的可以去看看。

解决top-k问题--堆排序-CSDN博客文章浏览阅读140次,点赞8次,收藏5次。堆排序将堆的根节点与最后一个节点交换,然后将堆的大小减1,并进行堆的重构。max1>max2,a[i]>max2,所以此时的max2的值已经不是这个数组次大的了。被淘汰说明有前k个数大于自己,加入top-k说明此时的a[i]至少大于目前的maxk。,如果大于此时的maxk,说明就目前来说,a[i]有资格进入这前k大的数,通常的做法是用一个变量max1依次去比较数组中的每一个数,并更新。,它的每个节点的值都大于等于(或小于等于)它的子节点的值。先把此时的maxk的值去掉,算上a[i]之后调整这k个数。https://blog.csdn.net/qq_62987647/article/details/134765613 

 2.3快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

 2.3.1递归版

对于单趟排序,我们确定一个基准值key,并利用双指针从首尾两端移动。找到一个位置放key,使得,key左边的元素都小于key(以升序排序为例),右边的元素都大于key

快排的算法思想本质是用空间换取时间,每一次递归排序的区间都在不断地缩小,每一趟排序都确定了下一趟排序的左右两个区间,类似于二叉树的节点的左右儿子节点。 

代码实现

int PartSort1(int* a, int left, int right) {//单趟排序int end = right;int begin = left;int mid = GetMid(a, left, right);//基准值的下标Swap(&a[left], &a[mid]);//跟首元素交换int key = left;while (begin < end) {while (end > begin && a[end] >= a[key])end--;while (end > begin && a[begin] <= a[key])begin++;Swap(&a[end], &a[begin]);}Swap(&a[key], &a[begin]);return begin;//单趟排序后返回最后key所处的位置
}
void QuickSort(int* a, int left, int right){//递归if (left >= right)return;int mid = PartSort3(a, left, right);QuickSort(a, left, mid - 1);QuickSort(a, mid + 1, right);
}

 2.3.2非递归版

利用,我们可以将单趟排序确定的两个子区间存起来,模拟函数栈帧的开辟。这样一来,我们就可以不用递归(递归较为耗损空间)就可以完成快速排序。

如果有对函数栈帧不了解的朋友可以去我之前写的博客里面看看。

深入理解函数调用--函数栈帧-CSDN博客文章浏览阅读87次,点赞6次,收藏3次。寄存器:寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。函数栈帧(Function Stack Frame)是在程序执行过程中,用来保存函数调用信息和局部变量的数据结构。它包含了函数的参数、返回地址和局部变量等信息。通俗的来说,就是在调用函数的时候系统给你开辟在栈区的一部分空间,专门用来存放局部变量等。https://blog.csdn.net/qq_62987647/article/details/132663764代码实现

void QuickSortNonR(int* a, int left, int right) {Stack ST;StackInit(&ST);StackPush(&ST, right);//根据栈的特性,先将右区间压栈StackPush(&ST, left);while (!StackEmpty(&ST)) {int l = StackTop(&ST);StackPop(&ST);int r = StackTop(&ST);StackPop(&ST);//取出一个区间[l,r]if (l > r)Swap(&l, &r);int keyi = PartSort1(a, l, r);//单趟排序if (keyi -1> l) {StackPush(&ST, keyi - 1);StackPush(&ST, l);}if (keyi + 1 < r) {StackPush(&ST, r);StackPush(&ST, keyi + 1);}}StackDestroy(&ST);
}

 2.4归并排序

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

实现思路: 

因为要先满足每个子序列都有序的条件,我们可以把区间长度划分为1,这样每个区间都一定是有序的,再对每个区间先上合并。值得注意的是,有序地子区间合并之后也是有序的。同样是根据二叉树的思想,每个区间一分为两个子区间,直到区间长度为1,需要logN的时间复杂度,合并两个区间又是N的复杂度,所以总的时间复杂度为NlogN

 代码实现

void MergeSort(int* a, int begin, int end, int* temp) {//temp用于暂时存放合并后的数组if (begin >= end)return;int mid = (begin + end) / 2;MergeSort(a, begin, mid, temp);MergeSort(a, mid + 1, end, temp);//先递归区间int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;//两个区间的端点指针int i = begin;//合并数组的下标的指针while (begin1 <= end1 && begin2 <= end2) {if (a[begin1] < a[begin2]) {//比较两个区间的首元素,小的放入temp中,两个指针往后移temp[i++] = a[begin1++];}else {temp[i++] = a[begin2++];}}//有可能还有某个区间还有元素没有放进去while (begin1 <= end1) {temp[i++] = a[begin1++];}while (begin2 <= end2) {temp[i++] = a[begin2++];}//将合并好的数组复制到原数组中memcpy(a + begin, temp + begin, sizeof(int) * (end - begin + 1));
}

3.排序算法的稳定性

对于某种排序算法,如果会将两个相同大小的元素的相对位置改变,那么我们就称这个算法是不稳定的,否者就是稳定的。 

什么时候需要考虑稳定性?

针对多个字段进行排序,就可能需要考虑排序算法的稳定性

 举例:

对以下数据进行排序:

序号订单金额订单时间
1509:04
2309:00
3509:03
4109:01

要求:

是按照订单金额进行升序排序,如果订单金额相同,则按照下单时间升序排序

先按照订单时间升序排序得到序号为:2、4、3、1

再从上一个序号组中按照订单金额升序排序

1.假如排序算法不稳定

则可能得到:4、2、1、3

对于序号1和3,订单金额相同,但是时间小的反而排在后面,不符合要求。

2.假如排序算法稳定

则一定得到:4、2、3、1

对于序号1和3,订单金额相同,下单时间大的排在后面,符合要求。

根据以上举例可以看出来,在对多个字段排序的时候,往往需要稳定的排序算法进行排序。

这也是为什么同样的时间复杂度,在有些时候能用不稳定的快排,有些时候用稳定归并排序。

 

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

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

相关文章

力扣2807.在链表中插入最大公约数

思路&#xff1a;遍历链表&#xff0c;对于每一个结点求出它与下一个结点的最大公约数并插入到俩个结点之间 代码&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}…

【数据结构】二叉树的概念及堆

前言 我们已经学过了顺序表、链表、栈和队列这些属于线性结构的数据结构&#xff0c;那么下面我们就要学习我们第一个非线性结构&#xff0c;非线性结构又有哪些值得我们使用的呢&#xff1f;那么接下来我们就将谈谈树的概念了。 1.树的概念与结构 1.1树的概念 树是一种非线性…

基于JavaWeb+SSM+Vue家政项目微信小程序系统的设计和实现

基于JavaWebSSMVue家政项目微信小程序系统的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关技术 2…

复旦MBA科创青干营(二期):探索合肥科创企业的创新之路

11月18日-19日&#xff0c;复旦MBA科创青干营二期学生开启了整合实践活动的第三次企业参访&#xff0c;前往位于合肥的蔚来第二先进制造基地、安徽万邦医药科技股份有限公司和合肥国轩高科动力能源有限公司&#xff0c;在学术导师和科创企业家“双导师”的指导下&#xff0c;深…

32.virtual reality system concepts illustrated using OSVR

32.1 Common Space This section describes the spaces needed to support viewing and interacting with the virtual world. 本节介绍支持查看虚拟世界并与之交互所需的空间。 The spaces required for supporting viewing and interacting with a virtual world can vary …

UAV | 多算法在多场景下的无人机路径规划(Matlab)

近年来&#xff0c;无人机(unmanned aerial vehicle&#xff0c;UAV)由于其灵活度高、机动性强、安全风险系数小、成本低等特点&#xff0c;被广泛应用于搜索巡逻、侦察监视、抢险救灾、物流配送、电力巡检、农业灌溉等军用或民用任务。路径规划是无人机执行任务的关键&#xf…

使用 Next.js 构建电商购物车

如今&#xff0c;无论你是经验丰富的开发人员还是完全的初学者&#xff0c;你都没有什么不能用 React 做的。 这主要是由于创建了诸如Next.js之类的工具&#xff0c;这些工具成功地简化了React前端开发。 因此&#xff0c;我们将探讨如何快速制作 Next.js 电子商务单页应用程序…

响应式开发

响应式开发的原理Bootstrap前端开发框架Bootstrap栅格系统阿里百秀首页案例 响应式开发原理 1 响应式需要一个父级做为布局容器&#xff0c;来配合子级元素来实现变化效果。 2 在不同屏幕下&#xff0c;通过媒体查询来改变这个布局容器的大小&#xff0c;再改变里面子元素的排…

内存 vs 硬盘:固态硬盘代替内存可以工作吗?

使用固态硬盘代替内存可以吗&#xff1f; 答案是​&#xff1a;不可以​。 ​这个问题看似复杂&#xff0c;其实包含很多方面的原因。 一、存储结构方面 固态硬盘和内存在存储结构上就完全不同。 1.1 固态硬盘采用的是3D闪存单元阵列来存储数据 这些存储单元被一层层地堆…

C++中的new和delete

相关文章 C智能指针 文章目录 相关文章前言一、new 运算符1. operator new 函数的范围2. 在类中重载new运算符3. 分配失败 二、delete 运算符1. 内存泄露统计示例2. 在类中重载delete运算符 总结 前言 在C中&#xff0c;new和delete是用于动态内存管理的运算符&#xff0c;它们…

【SpringCloud】设计原则之 DevOps 与无状态服务

一、设计原则之 DevOps DevOps 一词来自 Development 和 Operation 的组合&#xff0c;突出重视软件开发人员和运维人员的沟通合作&#xff0c;通过自动化流程来使得软件构建、测试、发布更加快捷、频繁和可靠。它要求开发、测试、运维进行一体化的合作&#xff0c;进行更小、…

关键词优化完整 “操作 “指南

关键词优化的定义 在内容中突出相关关键词的行为&#xff0c;有助于将谷歌流量引向您的网站。关键词优化要求内容创建者做到以下几点&#xff1a; 研究并发现最佳关键词找到自然的方式在内容中突出相关词语 看看&#xff0c;你已经创建了一些很棒的内容。你做了研究&#xf…