排序算法(详解)

排序在日常生活中十分重要,购物平台上商品的排序,各国高校等级的排序......可以说,现代生活中已经离不开排序了;因此学好排序算法至关重要,本篇文章就来讲讲常见的排序算法

排序的种类非常多,按照种类划分,有插入排序,选择排序,交换排序......,而每种排序中又分多种排序,下图是常见的排序算法

1.插入排序

1.1直接插入排序

算法思想:

假设数组中一个区间[0,end]中的数据有序了,插入end+1位置的数据,如何保持数据依然有序?

  • 将end+1位置的数据从后往前,依次与前面的数据比较,如果小于比较的数据,则将比较过的数据往后挪,直到找到小于它的数据或者找到头了;再在停下来的下一个位置插入数据
        //单趟排序int i = 0;int end;int tmp = a[end + 1];for (i = end + 1; i > 0; i--){if (tmp < a[i - 1])a[i] = a[i - 1];elsebreak;}a[i] = tmp;

注意:以后我们写有多趟逻辑的代码时,建议先写出单趟的逻辑,再加上整体的逻辑

上面是单趟排序,整体的排序,相当于依次对[0,0],[0,1]......,[0,n-1]每个区间都进行一次单趟排序

void InsertSort(int* a, int n)
{for (int j = 0; j < n - 1; j++){//单趟排序int i = 0;int end = j;int tmp = a[end + 1];for (i = end + 1; i > 0; i--){if (tmp < a[i - 1])a[i] = a[i - 1];elsebreak;}a[i] = tmp;}
}

复杂度分析:

  • 最好的情况:原数据有序或接近于有序,时间复杂度为O(N)
    最坏的情况:元数据无序或接近于无序,时间复杂度为O(N^{2} )
  • 空间复杂度:O(1)

1.2希尔排序

直接在数据有序或接近于有序的情况下效率是非常高的;但我们是不知道数据到底是怎么排序的,那能不能让数据先变成有序或接近于有序,再使用直接插入排序?这就是希尔排序的核心思想

算法思想:

希尔排序中,定义了一个间距gap,假设一开始gap为3,从第一个数据开始,将间距为gap的分为一组,一共有gap组

对每组分别使用直接插入排序,将每组排成有序,这样整体接近有序,再降低gap的值,重复操作,让数据更接近于有序,直到最后一次gap为1,此时就相当于直接插入排序了

首先是排一组中单趟的数据:

int i = 0;
int end;
int tmp = a[end + gap];
for (i = end + gap; i > gap - 1; i -= gap)
{if (tmp < a[i - gap])a[i] = a[i - gap];elsebreak;
}
a[i] = tmp;

再将一组排好

for (int j = 0; j < n - gap; j += gap)
{int i = 0;int end = j;int tmp = a[end + gap];for (i = end + gap; i > gap - 1; i -= gap){if (tmp < a[i - gap])a[i] = a[i - gap];elsebreak;}a[i] = tmp;
}

j<n-gap的原因同样是防止越界

排完第一组还要排后面的组,因此以gap为3的整体代码如下

	int gap = 3;for (int z = 0; z < gap; z++){for (int j = z; j < n - gap; j += gap){int i = 0;int end = j;int tmp = a[end + gap];for (i = end + gap; i > gap - 1; i -= gap){if (tmp < a[i - gap])a[i] = a[i - gap];elsebreak;}a[i] = tmp;}}

这个代码套了三层循环,其实可以优化一下

	int gap = 3;for (int j = 0; j < n - gap; j++){int i = 0;int end = j;int tmp = a[end + gap];for (i = end + gap; i > gap - 1; i -= gap){if (tmp < a[i - gap])a[i] = a[i - gap];elsebreak;}a[i] = tmp;}

如何理解呢?该代码是先将每组的前两个数据排好,再排每组的前三个数据......直到排好每组的最后一个数据

现在,我们gap组都排好了,需要减小gap的值,重复操作,并且保证最后一次排序gap为1

void ShellSort(int* a, int n)
{int gap = n;while (gap > 0){gap = gap / 2;for (int j = 0; j < n - gap; j++){int i = 0;int end = j;int tmp = a[end + gap];for (i = end + gap; i > gap - 1; i -= gap){if (tmp < a[i - gap])a[i] = a[i - gap];elsebreak;}a[i] = tmp;}}
}

怎么来确定gap的值呢?

我们发现,gap的值越大,大的数据跳到后面越快,小的数据跳到前面越快;gap越小,大的数据跳到后面越慢,小的数据跳到前面越慢

怎么取gap的值才最合适呢?其实也没有一个标准的说法,最关键的是你得保证最后一次排序gap的值为1

复杂度分析:

时间复杂度:O(N^{1.25})\sim O(1.6*N^{1.25} )

空间复杂度:O(1)


2.选择排序

2.1选择排序

算法思想:

遍历数据,选出最大的和最小的,换到尾和头,再选出次大的和次小的......

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void SelectSort(int* a, int n)
{int begin = 0;int end = n - 1;while (begin < end){//单趟排序int mini = begin;int maxi = begin;for (int i = begin; i <= end; i++){if (a[i] < a[mini])mini = i;if(a[i] > a[maxi])maxi = i;}Swap(&a[begin], &a[mini]);if (maxi == begin)maxi = mini;Swap(&a[end], &a[maxi]);begin++;end--;}
}

复杂度分析:

  • 时间复杂度:
    最好的情况:O(N^{2} )
    最坏的情况:O(N^{2} )
  • 空间复杂度:O(1)

2.2堆排序

堆排序的讲解在这篇文章中堆排序(详解)-CSDN博客

这里就不再花时间细说了

复杂度分析:

  • 时间复杂度:O(N*\log_{2}{N} )
  • 空间复杂度:O(1)

3.交换排序

3.1冒泡排序

算法思想:

每一趟将一个数放到它应该在的位置,N个数需要进行N-1趟;由于该排序较简单,这里也不细讲了

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

复杂度分析:

  • 时间复杂度:
    最好的情况:O(N)
    最坏的情况:O(N^{2} )
  • 空间复杂度:O(1)

3.2快速排序

算法思想:

如果一个数的左边的数都比它小,右边的数都比它大,那么这个数是不是就在它应该在的位置;快速排序的单趟排序就是将一个数变成具有上述性质;再去递归该数的左区间和右区间

快速排序的单趟排序有三种版本

第一种:hoare版

  • 随便选择一个数作为要调的整数,记为key;right从右往左找比key小的数,left从左往右找比key大的数,一旦找到就停下来,交换left和right位置的数,直到left和right相遇
    //单趟排序int left = begin;int right = end;int keyi = left;//keyi是key的下标while (left < right){while (left < right && a[right] >= a[keyi]){right--;}while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);keyi = left;
  • 为什么判断大小时要加等号?
    我们一开始的key在left的位置,如果不加等于,第一次交换会改变key的值
  • 为什么判断的时候要left<right?
    在找的过程中可能left超过了right,此时应当终止循环
  • 为什么是右边先走?
    右边先走停下来的情况有两种:1)遇到比key小的数;2)和begin相等了
    由于right先走了,所以下次right走时,left位置的数一定是比key小的
    也就是说right停下来的位置的数一定是比key要小的,交换key和left与right相遇的位置,key左边就都比它小,右边都比它大了
    如果是left先走,left和right相遇时,相遇的位置的数可能比key大,此时和key交换的话,就不符合我们的要求了,因此右边需要先走

此时数据被分成了三个区间[begin,keyi-1]keyi[keyi+1,end],我们再对[begin,keyi-1]和[keyi,end]区间递归,如果begin>=end就直接返回

void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int left = begin;int right = end;int keyi = left;//keyi是key的下标while (left < right){while (left < right && a[right] >= a[keyi]){right--;}while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);keyi = left;QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

第二种:挖坑法

将随便一个位置的数记录为key,这里就选开始位置的数,作为一个坑位;同样的右边先走,找比key小的数,不同的是,这时找到了就把那个数放到刚才的坑位,留下了另一个坑位;再左边找比key大的数,找到了交换到上一个坑位,留下一个坑位......直到left和right相遇,将key给到上一个坑位

int PartSort2(int* a, int begin, int end)
{int left = begin;int right = end;int keyi = left;int key = a[left];while (left < right){while (left < right && a[right] >= key){right--;}a[left] = a[right];while (left < right && a[left] <= key){left++;}a[right] = a[left];}a[left] = key;keyi = left;return keyi;
}

第三种:前后指针法

定义两个指针prev和cur和key;cur去遍历数据,如果cur位置的数大于key,cur++;如果cur位置的数小于key,prev++之后交换perv和cur位置的数,直到cur走到尾;再交换perv和key的值

该方法的本质是让prev和cur错开,让它们之间的数都是比key大的数,再将比key小的数与prev和cur中间的数交换,相当于把中间的数往后挪

int PartSort3(int* a, int begin, int end)
{int prev = begin;int cur = begin + 1;int keyi = begin;int key = a[begin];while (cur <= end){if (a[cur] < key){prev++;Swap(&a[prev], &a[cur]);}cur++;}Swap(&a[keyi], &a[prev]);keyi = prev;return prev;
}

3.2.1快排的优化

优化一:三数取中

由于快排是递归进行排序的,如果每次key是中间数,那么需要递归的层数是\log_{2}{N}层;如果数据有序或接近于有序,递归的层数就会接近N层,效率大大降低,还可能会导致栈溢出,因此我们添加一个三数取中算法,确保每次key取到的不是最大或最小数

int GetMidi(int* a, int begin, int end)
{int midi = (begin + end) / 2;if (a[begin] < a[end]){if (a[midi] < a[begin])return begin;else if (a[end] < a[midi])return end;elsereturn midi;}else{if (a[midi] < a[end])return end;else if (a[begin] < a[midi])return begin;elsereturn midi;}
}

优化二:部分递归换直接插入排序

如果只有10个数,用递归去排序是不是显得很繁琐,因为递归还要建立栈帧,这时可以考虑用其他排序,我们选择了直接插入排序

void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;if (end - begin + 1 <= 10){InsertSort(a + begin, end - begin + 1);}else{//int keyi = PartSort1(a, begin, end);//int keyi = PartSort2(a, begin, end);int keyi = PartSort3(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);}
}

复杂度分析:

  • 时间复杂度:O(N*\log_{2}{N})
  • 空间复杂度:O(1)

3.3非递归的快速排序

非递归的快排本质是将要排序的区间存到一个栈中,选一种单趟排序,排完后数据分成了三部分,将右区间和左区间入栈,进行排序,直到栈为空

void QuickSortNonR(int* a, int begin, int end)
{Stack st;StackInit(&st);StackPush(&st, end);StackPush(&st, begin);while (!StackEmpty(&st)){int left = StackTop(&st);StackPop(&st);int right = StackTop(&st);StackPop(&st);int keyi = PartSort3(a, left, right);//[left,keyi-1]keyi[keyi+1,right]if (keyi + 1 < right){StackPush(&st, right);StackPush(&st, keyi + 1);}if (left < keyi - 1){StackPush(&st, keyi - 1);StackPush(&st, left);}}StackDestroy(&st);
}

4.归并排序

算法思想:

如果一串数据中左边一部分有序了,右边一部分也有序了,那么把整体弄成有序?这就是合并两个有序数组的问题了

那怎么让左边和右边有序呢?将左边数据也弄成两部分,只要这两部分有序,再对整体使用合并算法,整体就有序了,右边也是同理

像这样一直分,直到两部分都只有一个数据,此时每部分相当于有序,合并后返回;也就是说,归并排序也是用递归来实现的

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

复杂度分析:

  • 时间复杂度:O(N*\log_{2}{N} )
  • 空间复杂度:O(N)

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

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

相关文章

分割均衡字符串 - 华为OD统一考试(C卷)

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 题目描述 均衡串定义:字符串只包含两种字符&#xff0c;且两种字符的个数相同。 给定一个均衡字符串&#xff0c;请给出可分割成新的均衡子串的最大个数。 约定字符串中只…

汇川SV660P伺服MODBUS通信设置回原

汇川伺服MODBUS-RTU通信的详细设置可以查看下面文章链接: https://rxxw-control.blog.csdn.net/article/details/134942690https://rxxw-control.blog.csdn.net/article/details/134942690首先我们介绍最简单的回原方式,就是以当前位置为原点回原,此时伺服电机不动作,绝对…

Layui实现自定义的table列悬停事件并气泡提示信息

1、概要 使用layui组件实现table的指定列悬停时提示信息&#xff0c;因为layui组件中没有鼠标悬停事件支持&#xff0c;所以需要结合js原生事件来实现这个功能&#xff0c;并结合layui的tips和列的templte属性气泡提示实现效果。 2、效果图 3、代码案例 <!DOCTYPE html&g…

智能优化算法应用:基于乌鸦算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于乌鸦算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于乌鸦算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.乌鸦算法4.实验参数设定5.算法结果6.参考文献7.MA…

vue2-安装elementUI时警告

警告内容&#xff1a;npm WARN deprecated core-js2.6.12: core-js<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up …

微软免费开发隧道(内网穿透)服务

软件下载 Windows x64&#xff1a;https://aka.ms/TunnelsCliDownload/win-x64 macOS (arm64)&#xff1a;https://aka.ms/TunnelsCliDownload/osx-arm64-zip macOS (x64)&#xff1a;https://aka.ms/TunnelsCliDownload/osx-x64-zip Linux x64&#xff1a;https://aka.ms/…

漏电保护芯片——可直接驱动SCR,输出脉宽模式可选择。适用于交流110V~220V(50~60Hz)供电系统

D4140 D4145 D4147 D54123等芯片适用于交流110V~220V(50~60Hz)供电系统&#xff0c;用于检测AC型剩余漏电信号&#xff0c;可直接驱动SCR&#xff08;可控硅&#xff09;&#xff0c;当有漏电信号时&#xff0c;OS输出高电平延时脉冲&#xff0c;输出脉宽模式可选择。

docker安装RabbitMQ3.8.34

&#x1f60a; 作者&#xff1a; 瓶盖子io &#x1f496; 主页&#xff1a; 瓶盖子io-CSDN博客 a.拉取rabbitmq镜像 docker pull rabbitmq:3.8.34-management b.根据镜像创建容器 在yml配置文件中port是5672 访问是ip:15672 docker run --name rabbitmq -d -p 5672:5672 -…

JavaScript如果实现一段文字的高亮显示

JavaScript实现将一段文字检索高亮显示,效果如下: 实现方法:调用highlight函数 highlight("JavaScript如果实现一段文字的高亮显示", "高亮")实战代码实现: function highlight(value, search)

node.js express JWT token生成与校验

目录 JWT header&#xff08;标头&#xff09; payload&#xff08;有效负载&#xff09; signature&#xff08;签名&#xff09; 访问令牌&#xff08;token&#xff09; express jwt生成、验证 生成jwt 验证jwt JWT JWT 是轻量级的数据交换格式&#xff0c;相对于传…

5分钟搞懂Kubernetes:轻松理解所有组件

Kubernetes 首先&#xff0c;我想强调的是&#xff0c;在学习任何一项知识时&#xff0c;官方文档都是最重要的资源&#xff1a;Kubernetes 文档 | Kubernetes 官方文档提供了详尽、准确的信息&#xff0c;帮助我们深入了解和掌握这个技术。因此&#xff0c;如果你真的对Kubern…

Jenkins 添加节点报错

报错日志 Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.lang.UnsupportedClassVersionError: hudson/remoting/Launcher has been compiled by a more recent version of the Java Runtime (cl…

Python接口测试 requests.post方法中data与json参数区别

引言 requests.post主要参数是data与json&#xff0c;这两者使用是有区别的&#xff0c;下面我详情的介绍一下使用方法。 Requests参数 1. 先可以看一下requests的源码&#xff1a; def post(url, dataNone, jsonNone, **kwargs):r"""Sends a POST request.…

c# 字段和属性(get、set、init)

目录 基本概念&#xff1a; 个人理解&#xff1a; 访问器的默认写法&#xff1a; set与init无法共存&#xff1a; init&#xff1a; 必须在类的实例化时给字段赋值的情况(require属性): 基本概念&#xff1a; “字段”就是类内成员变量&#xff0c;一般为了隐藏数据&…

抗EMC干扰高精度隔离放大器ISO EC 系列

SunYuan ISO EC系列模拟信号隔离放大器是一种有较强抗EMC干扰特性的电容耦合隔离混合集成电路。该放大器采用了全新的调制-解调电容耦合隔离技术&#xff0c;模块中信号以数字信号的方式通过电容隔离层进行传输&#xff0c;通过数字调制和电容耦合隔离方式保持信号的完整性&…

【深度学习】注意力机制(三)

本文介绍一些注意力机制的实现&#xff0c;包括EMHSA/SA/SGE/AFT/Outlook Attention。 【深度学习】注意力机制&#xff08;一&#xff09; 【深度学习】注意力机制&#xff08;二&#xff09; 【深度学习】注意力机制&#xff08;四&#xff09; 【深度学习】注意力机制&a…

Google为什么它还没有开发出ChatGPT,如何反超,小公司创新的产品如何反超巨头 行业巨头如何防止被小公司或创新型公司的产品超越

Google虽然收购了Geoffrey Hinton及其在多伦多大学的两名研究生组成的公司DNNresearch Inc.&#xff0c;但为什么它还没有开发出类似ChatGPT的产品&#xff0c;可能有以下几个原因&#xff1a; 不同的研发方向&#xff1a;Google在人工智能领域的研发方向可能与OpenAI&#xff…

大创项目推荐 深度学习 opencv python 公式识别(图像识别 机器视觉)

文章目录 0 前言1 课题说明2 效果展示3 具体实现4 关键代码实现5 算法综合效果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的数学公式识别算法实现 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学…

基于Spring Boot+Vue.js的停车场收费管理系统 需求分析

1 用户&#xff08;收费员&#xff09; 1.1 主页 1.1.1 摄像头实时捕捉画面&#xff0c;如果有车牌号则识别出车牌&#xff08;如&#xff1a;京A11111&#xff09;&#xff0c;通过车牌底色识别出小型车&#xff08;蓝色&#xff09;、大型车&#xff08;黄色&#xff09;。…

《对话品牌》——科技与时尚的融合

本期节目《对话品牌》栏目组邀请到了江西先禾服饰有限公司董事长吁火兰女士参加栏目录制&#xff0c;分享其企业故事&#xff0c;树立品牌形象&#xff0c;提升品牌价值&#xff01; 节目嘉宾&#xff1a;吁火兰 节目主持人&#xff1a;杨楠 节目播出平台&#xff1a;中央新…