交换排序实现

文章目录

  • 冒泡排序
  • 快速排序
    • 快排的优化
    • 单次快排的其他方案
    • 快排的非递归实现

冒泡排序

冒泡排序,Bubble sort,通过重复遍历要排序的数列,比较每一对相邻元素,并在顺序错误时交换它们。这个过程一直重复,直到没有需要交换的相邻元素为止。

也就是每一次选出一个最大值,然后由此排序.
代码实现:

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

性能分析:冒泡排序的时间复杂度是标准的 O ( N 2 ) O(N^2) O(N2),最好情况下是顺序有序,时间复杂度为 O ( N ) O(N) O(N)
由于冒泡排序性能的局限性,在实际场景的应用性几乎为零(有没有应用我是不清楚的), 其教学意义大于其实际意义,属于是最容易理解的排序算法。

快速排序

快速排序,Quick sort,它的基本思想是通过选取一个基准元素,将数组分为两部分,一部分小于基准元素,一部分大于基准元素,然后对这两部分分别进行递归排序,最终得到一个有序的数组。

通过上面的分析我们知道,快速排序选择一个基准值key,然后让key的左边的数小于等于key,右边的数大于等于key。那么我们通过一次排序后就决定好了一个元素的位置也就是key的位置。然后再对两部分进行递归排序。
这个思想明显有着二叉树递归的影子,key最好的情况下在确定在数组中间,再将key左边的数组(左子树)和右边的数组(右子树)递归。那么递归的深度就是二叉树的高度也就是 l o g N logN logN,每一层要遍历元素个数就是N,N-1,N-2…
代码实现:

void QuickSort(int* a, int begin, int end)
{if (begin >= end){return;}int left = begin, right = end;int keyi = begin;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[left], &a[keyi]);QuickSort(a, begin, left - 1);QuickSort(a, left + 1, end);
}

性能分析:时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)
观察上述代码,值得注意的有以下几点:

  • keyi即为key值对应的下标,一般选最左边的值也就是begin。
  • 首先right这个下标找的是比key小的值,那么必须是right先走,这样才确保确保最后left和right相遇的时候下标对应的数组元素比key小或者相等
    分析:left和right相遇有以下两种情况:
    1 right遇上left,那么left一开始就在等于key的地方。或者执行了Swap,那么left对应的数组值就是小于key的。
    2.left遇上right,既然left可以移动了那就是说right已经找到了比key小的值,所以相遇的下标对应的数组元素也是比key小。
    综上所述,当right先移动时,最终left==right时对应的下标的数组元素小于或等于key。
  • left初始化不可以取begin+1有些初学者认为,begin不就是key值本身对应的下标么,那么我们和不从begin+1开始比较?有这种思想是正常的,但也是错误的。只要是任何一种顺序有序(或者其他情况),那么这种快排都是错的。
    例如:数组int arr[]={1,2,3},left=1,right=2,keyi=0.那么left和right相遇在1,最后得到的结果为{2,1,3}。显然错误。
  • left和right移动前要判断left < right ,防止越界。
  • a[right] >= a[keyi]和a[left] <= a[keyi]这两个判定条件时要写的,而非a[right] > a[keyi]和a[left] < a[keyi],否则left和right大概率不会相遇了。T.T

快排的优化

  • 细心的读者已经发现了,当数组a为顺序有序的时候,快排的时间复杂度竟然是 O ( N 2 ) O(N^2) O(N2),而且还要不断递归申请空间,这时候甚至比不上BubbleSort了。这是因为我们keyi默认取最开始的值,又因为顺序有序所以key排在最左边,这样左边部分数组为空,右边部分数组大小为N-1。然后就N-2,N-3…递归。
    所以优化方法也很简单我们换一种取keyi的方式,这里有个参考方案如下:
int GetMidi(int* a, int left, int right)
{int midi = (left + right) / 2;if (a[left] > a[midi]){if (a[midi] > a[right])return midi;else if (a[right] > a[left])return left;elsereturn right;}else{if (a[left] > a[right])return left;else if (a[right] > a[midi])return midi;elsereturn right;}
}
void quicksort(int* a, int begin, int end)
{if (begin >= end){return;}int left = begin, right = end;int keyi = begin;swap(&a[begin], &a[getmidi(a, left, right)]);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[left], &a[keyi]);quicksort(a, begin, left - 1);quicksort(a, left + 1, end);
}

这样我们就得到了一个较优的keyi取值。

  • 此外,通过二叉树的结构我们知道,最下面几层的结点个数占所有结点个数的80%以上。也就是说当end-begin+1(数组长度)较小的时候,需要递归的次数就会较多。那么当长度较小的时候我们可以采用其他排序方式来大大减少递归次数。
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 left = begin, right = end;int keyi = begin;swap(&a[begin], &a[getmidi(a, left, right)]);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[left], &a[keyi]);quicksort(a, begin, left - 1);quicksort(a, left + 1, end);}
}

上述代码中,当数组长度小于10的时候我们选择用直接插入排序。
事实上这个优化可有可无,当今编译器优化做的已经非常好了多递归这几次和少递归这几次区别也不大。

单次快排的其他方案

上述快排是由霍尔提出的,但显然这个初代的版本要注意的细节太多。以下还有两种其他方式实现单词快排。
1.挖坑法

int PartSort2(int* a, int begin, int end)
{Swap(&a[begin], &a[GetMidi(a, begin, end)]);int key = a[begin];int hole = begin;while (begin < end){while (begin < end && a[end] >= key){end--;}a[hole] = a[end];hole = end;while (begin < end && a[begin] <= key){begin++;}a[hole] = a[begin];hole = begin;}a[hole] = key;return hole;
}

其实这种实现方式和Hoare的实现大同小异,就不过多赘述请自行观摩。
2.前后指针法:

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

这种方法是通过cur找出比key小的值,再和prev交换然后prev++。
我个人认为这种方式是较上面两种方式简洁的而且也不会有那么多陷阱。

快排的非递归实现

要将一个递归的代码转换成非递归一般有以下两种方式:
1.转换成迭代(那说明这个代码本身就可以用循环写)
2.借助栈(先进先出的性质)来实现非递归。
代码如下:

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

假设这么一个数组有100个元素,并且每次key值都排到了中间。
那么上述代码就是说我先压栈了99,0.那么就可以取出left和right。拍好之后找个keyi=55.
随后压栈54,0,99,55.就可以取出右部分数组的begin和right。得到keyi=77.
压栈76,55,99,78.又得到了右部分数组。

就这样我们实现了用栈模拟出递归的效果

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

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

相关文章

动态规划 Leetcode 1049 最后一块石头的重量

最后一块石头的重量 Leetcode 1049 学习记录自代码随想录 要点&#xff1a;1.想到求最后剩下石头的重量可以转化为求石头组合重量最接近石头重量总和的一半和此时剩下石头重量的差值&#xff0c;从而联想到Leetcode 416此题便可以相同套路求解 class Solution { public:int…

Kafka MQ 生产者

Kafka MQ 生产者 生产者概览 尽管生产者 API 使用起来很简单&#xff0c;但消息的发送过程还是有点复杂的。图 3-1 展示了向 Kafka 发送消息的主要步骤。 我们从创建一个 ProducerRecord 对象开始&#xff0c;ProducerRecord 对象需要包含目标主题和要发送的内容。我们还可以…

windows ffmpeg 编译环境搭建

编译ffmpeg https://www.msys2.org/ https://www.ffmpeg.org/platform.html#Microsoft-Visual-C_002b_002b-or-Intel-C_002b_002b-Compiler-for-Windows 1.安装msys2 2.安装yasm或者nasm 打开VC 本地环境命令行 唤醒msys2界面 配置编译环境变量参数 export PATH"/d/vs…

Dynamo处理Excel——调用Microsoft.Office.Interop.Excel教程

你好&#xff0c;这里是BIM的乐趣&#xff0c;我是九哥~ 今天我们来聊聊如何通过Dynamo处理Excel数据以及格式&#xff0c;Dynamo自带的节点肯定是不行&#xff0c;所以我们需要来用Python解决&#xff08;当然有个节点包 Bumblebee&#xff0c;我在案例百解教程里有过介绍&a…

c++虚函数表学习

1 基础和示例1 每个包含了虚函数的类都包含一个虚表,类中只要有虚函数,就会有一个虚表来维护。 虚表是属于类的,而不是属于某个具体的对象;同一个类的所有对象都使用同一个虚表。 虚表是一个指针数组,其元素是虚函数的指针,每个元素对应一个虚函数的函数指针; 普通的函…

如何实现sam(Segment Anything Model)|fastsam模型

sam是2023年提出的一个在图像分割领域的大模型&#xff0c;其具备了对任意现实数据的分割能力&#xff0c;其论文的介绍可以参考 https://hpg123.blog.csdn.net/article/details/131137939&#xff0c;sam的亮点在于提出一种工作模式&#xff0c;同时将多形式的prompt集成到了语…

Spring核心接口:ObjectProvider接口

ObjectProvider 是 Spring Framework 5.0 中引入的一个接口&#xff0c;用于提供对 bean 的延迟访问。它可以用于在需要延迟获取 bean 或在需要对 bean 进行多次访问时&#xff0c;减少 bean 的创建次数和提高应用程序性能。ObjectProvider 接口有两个主要方法&#xff1a;getO…

短剧在线搜索源码(全网首发)

一个非常哇塞的在线短剧搜索页面&#xff0c;接口已经对接好了&#xff0c;上传源码到服务器解压就能直接用&#xff0c;有能力的可以自己改接口自己写自己的接口 接口文档地址&#xff1a;doc.djcat.sbs 源码下载地址&#xff1a;https://pan.xunlei.com/s/VNstN8C6N3VK1a1k…

使用Docker在windows上安装IBM MQ

第一步、安装wsl 详见我另一篇安装wsl文章。 第二步、安装centos 这里推荐两种方式&#xff0c;一种是从微软商城安装&#xff0c;一种是使用提前准备好的镜像安装&#xff0c;详见我另一篇windos下安装centos教程。 第三步、安装windows下的Docker desktop 详见我另一篇wind…

【牛客】HJ62 查找输入整数二进制中1的个数

题目链接&#xff1a;查找输入整数二进制中1的个数_牛客题霸_牛客网 (nowcoder.com) 解题思路&#xff1a; 解法一: 可以举一个八位的二进制例子来进行分析&#xff0c;对于二进制操作&#xff0c;我们直到&#xff0c;除以一个2&#xff0c;原来的数字会减少一个0.如果除的过程…

分布式ID(8):分布式ID生成方法

在分布式系统中,生成唯一的ID是一个核心问题,特别是在需要确保数据完整性和避免冲突的场景中。以下是对五种分布式唯一ID生成方法的详细阐述,包括它们的工作原理、优缺点,以及对网络依赖性的考量: 1 UUID(通用唯一标识符) 实现原理 工作方式:UUID是通过一系列算法生成…

day15:无重叠区间

问题描述&#xff1a; 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量&#xff0c;使剩余区间互不重叠 。 示例 1: 输入: intervals [[1,2],[2,3],[3,4],[1,3]] 输出: 1 解释: 移除 [1,3] 后&#xff0c;剩下的…

Window API 使用的一些注意事项

文章目录 1、LPCWSTR类型2、LPCTSTR类型3、LPCSTR类型4、LPCTSTR和LPCWSTR区别5、LPCTSTR和LPCSTR、LPCWSTR三者区别6、_T(" ")7、DWORD类型转换为std::wstring类型8、char类型转换为LPCSTR类型9、获取当前时间戳(毫秒)10、std::wstring和LPCSTR区别11、std::wstring…

2024年【危险化学品生产单位安全生产管理人员】复审考试及危险化学品生产单位安全生产管理人员模拟试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品生产单位安全生产管理人员复审考试根据新危险化学品生产单位安全生产管理人员考试大纲要求&#xff0c;安全生产模拟考试一点通将危险化学品生产单位安全生产管理人员模拟考试试题进行汇编&#xff0c;组成…

Midjourney新算法来袭!解决你角色形象一致性的大难题——亲测猫与女孩跨场景表现

嘿&#xff0c;朋友们&#xff0c;你们想过这个问题吗&#xff1f; 当你在制作一部电影或写一部小说时&#xff0c;你总希望同一个角色能在不同的场景和背景下出现&#xff0c;对吧&#xff1f; 但这时&#xff0c;一个难题冒出来了&#xff1a;如何确保这个角色的形象在各个…

AWS的CISO:GenAI只是一个工具,不是万能钥匙

根据CrowdStrike的年度全球威胁报告,尽管研究人员预计人工智能将放大对防御者和攻击者的影响,但威胁参与者在其行动中使用人工智能的程度有限。该公司上个月在报告中表示:“在整个2023年,很少观察到GenAI支持恶意计算机网络运营的开发和/或执行。” 对于GenAI在网络安全中的…

专升本 C语言笔记-08 goto语句

goto语句 无条件跳转运算符(凡是执行到goto语句会直接跳转到 定义的标签) 缺点&#xff1a;滥用goto语句将会导致逻辑混乱&#xff0c;导致系统崩溃等问题! ! ! 代码演示 int i 0; //定义标签 jump(名字随便起哦) jump:printf("%d ",i); i; if(i < 10)goto j…

大数据面试

为什么有外部表和内部表&#xff1f;区别是什么&#xff1f; 早期也是没有内部表和外部表的说法的&#xff0c;在传统的RDMS数据数据库领域中&#xff0c;如果想要对一个数据库表进行例如增删改查等操作&#xff0c;就需要在数据库引擎中规范建立对应的数据库表&#xff0c;并…

Android Studio字体大小调节

外观页面字体调节 settings->Appearance->User cunstom font 代码字体调节 Settings->Editor->Font此时logcat窗口、Build窗口和Ternimal窗口字体大小也会同步调节&#xff08;2023.2.1版本上验证&#xff09;

在 Amazon Bedrock 上使用 Anthropic Claude 系统 Prompt

系统 prompt 是定义生成式 AI 模型对用户输入的响应策略的一种好方法。这篇博文将介绍什么是系统 prompt&#xff0c;以及如何在基于 Anthropic Claude 2.x 和 3 的应用中使用系统 prompt。 亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例…