【交换排序】冒泡排序 与 快速排序

交换排序基本思想:

所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

目录

1.冒泡排序

2.快速排序

2.1 递归实现 

2.2 快速排序优化

2.3 非递归实现


1.冒泡排序

假设升序。每次遍历,两两比较,将大的元素向后交换,直到选出最大的元素放在最后,这时已经确定了升序中最后一个元素,然后多次遍历前面无序的元素,每次可以确定一个最大的数,直到排序完成。

动态图解:

代码实现:

//交换函数
void Swap(int* p1, int* p2)
{int t = *p1;*p1 = *p2;*p2 = t;
}void BubbleSort(int* arr, int n)
{for (int i = 0; i < n - 1; i++){for (int j = 0; j < n - 1 - i; j++){if (arr[j] > arr[j + 1]){Swap(&arr[j], &arr[j + 1]);}}}
}

如果在排序有序数据时,我们还可以优化一下,提高一下效率,代码如下

void BubbleSort(int* arr, int n)
{for (int i = 0; i < n - 1; i++){int flag = 1;//假设已经有序for (int j = 0; j < n - 1 - i; j++){if (arr[j] > arr[j + 1]){Swap(&arr[j], &arr[j + 1]);flag = 0;//发生交换,说明无序}}if(flag == 1)//已经有序,不在继续排序{break;}}
}

冒泡排序的特性总结:

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

2.快速排序

2.1 递归实现 

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

图示:

代码实现:

假设按照升序对array数组中【left, right】左右都是闭区间中的元素进行排序。

void QuickSort(int* a, int left, int right)
{if (left >= right){return;}//按照基准值(中间位置)对array数组的[left, right]区间中的元素进行划分int keyi = PartSort(a, left, right);//划分成功后以div为边界形成了左右两部分[left, keyi-1]和[key+1,right]//递归排 [left, keyi-1]QuickSort(a, left, keyi - 1);//递归排[key+1,right]QuickSort(a, keyi + 1, right);
}

上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,大家在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

将区间按照基准值划分为左右两半部分(PartSort)的常见方式有:

1. hoare 版本

我们先看一下动图,方便理解 

巧妙之处: 

  1. 左边做key,右边先走;保障了相遇位置的值比key小,或者就是key的位置
  2. 右边做key,左边先走;保障了相遇位置的值比key大,或者就是key的位置

我们这里使用第一种方法:

L和R相遇无非就是两种情况,L遇R和R遇L:

  1. 情况一:L遇R,R是停下来,L在走,R先走,R停下来的位置一定比key小,相遇的位置就是R停下的位置,就一定比key要小
  2. 情况二:R遇L,在相遇这一轮,L就没动,R在移动,跟L相遇,相遇位置就是L的位置,L的位置就是key的位置或者这个位置交换过,换成了比key小的,相遇L位置一定比key小

代码实现:

int PartSort1(int* a, int left, int right)
{int keyi = left;while (left < right){//右边找小 与key相等的数据放在左边右边都可以while (left < right && a[right] >= a[keyi]){right--;}//左边找大 与key相等的数据放在左边右边都可以while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[right]);return right;
}

2. 挖坑法

 left 和 right 中有一个一定是坑位,右边找小,左边找大,找到就将值放入原坑位,该位置成为新坑位。

代码实现:

int PartSort2(int* a, int left, int right)
{int hole = left;int key = a[left];while (left < right){//右边找小while (left < right && a[right] >= key){right--;}a[hole] = a[right];hole = right;//左边找大while (left < right && a[left] <= key){left++;}a[hole] = a[left];hole = left;}a[hole] = key;return hole;
}

3. 前后指针版本

  1. 最开始prev和cur相邻的
  2. 当cur遇到比key的大的值以后,他们之间的值都是比key大的值
  3. cur找小,找到小的以后,跟++prev位置的值交换,相当于把大翻滚式往右边推同时把小的换到左边
     

 代码实现:

int PartSort3(int* a, int left, int right)
{int keyi = left;int prev = left;int cur = left + 1;while (cur <= right){if (a[cur] <= a[keyi]&&++prev!=cur)//自己不与自己交换{Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[prev], &a[keyi]);return prev;
}

2.2 快速排序优化

当我们遇到有序数据时,由于我们的key是选的第一个元素,时间复杂度会变成O(N^2)。有两种优化方法:

  1. 三数取中法选key
  2. 随机数选key

 我们这里讲一下第一种方法:让三个数作比较,取中间值

三数取中,代码实现:

int GetMidIndex(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if(a[left]>a[right]){return left;}else{return right;}}else//a[left]>a[mid]{if (a[mid] > a[right]){return mid;}else if (a[left] > a[right]){return right;}else{return left;}}
}

然后我们需要在上面3种划分方式开头都加上下面代码,就可以达到优化的目的

int midi = GetMidIndex(a, left, right);
Swap(&a[midi], &a[left]);

2.3 非递归实现

我们这里使用栈来实现,栈内存放需要划分的区间端点值,利用栈先入后出的特点模拟实现递归

void QuickSortNonR(int* a, int left, int right)
{Stack st;StackInit(&st);//入栈StackPush(&st, right);StackPush(&st, left);while (!StackEmpty(&st)){left = StackTop(&st);StackPop(&st);right = StackTop(&st);StackPop(&st);int keyi = PartSort1(a, left, right);//想先处理左边,就先右边区间先入栈//以基准值为分割点,形成左右两部分:[left, keyi-1]和[keyi+1,right)if(right > keyi + 1){StackPush(&st, right);StackPush(&st, keyi + 1);}if (left < keyi - 1){StackPush(&st, keyi - 1);StackPush(&st, left);}}StackDestroy(&st);
}

当然也可以使用队列来模拟。队列相当于广度优先,二叉树中的层序遍历,栈是深度优先,二叉树的先序遍历。

快速排序的特性总结:

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

本篇结束!我们下一篇文章来学习排序第四课【归并排序】。

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

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

相关文章

OptaPlanner笔记5

2.4 与spring boot集成 2.4.4 添加依赖 <dependency><groupId>org.optaplanner</groupId><artifactId>optaplanner-spring-boot-starter</artifactId> </dependency>2.4.8 创建求解器服务 import org.optaplanner.core.api.solver.Solv…

Django笔记之数据库函数之日期函数

日期函数主要介绍两个大类&#xff0c;Extract() 和 Trunc() Extract() 函数作用是提取日期&#xff0c;比如我们可以提取一个日期字段的年份&#xff0c;月份&#xff0c;日等数据 Trunc() 的作用则是截取&#xff0c;比如 2022-06-18 12:12:12&#xff0c;我们可以根据需求…

CSS:弹性盒子模型详解(用法 + 例子 + 效果)

目录 弹性盒子模型flex-direction 排列方式 主轴方向换行排序控制子元素缩放比例缩放是如何实现的&#xff1f; 控制子元素的对其方式justify-content 横向 对齐方式align-items 纵向 对齐方式 align-content 多行 对齐方式 弹性盒子模型 flex-direction 排列方式 主轴方向 f…

CI+JUnit5并发单测机制创新实践

目录 一. 现状问题 二. 分析原因 三. 采取措施 四. 实践步骤 五. 效能提升 资料获取方法 一. 现状问题 针对现如今高并发场景的业务系统&#xff0c;“并发问题” 终归是必不可少的一类&#xff08;占比接近10%&#xff09;&#xff0c;每次出现问题和事故后&#xff0c…

CNN经典网络模型之GoogleNet论文解读

目录 1. GoogleNet 1.1 Inception模块 1.1.1 1x1卷积 1.2 辅助分类器结构 1.3 GoogleNet网络结构图 1. GoogleNet GoogleNet&#xff0c;也被称为Inception-v1&#xff0c;是由Google团队在2014年提出的一种深度卷积神经网络架构&#xff0c;专门用于图像分类和特征提取任…

vConsole手机调试模式uniapp和原生h5

手机打开调试模式的方法 尽量放在页面头部&#xff0c;底部有可能不行 原生 <script src"https://cdn.bootcss.com/vConsole/3.3.0/vconsole.min.js"></script> <script>// init vConsolevar vConsole new VConsole();console.log(Hello world)…

ElasticSearch安装与启动

ElasticSearch安装与启动 【服务端安装】 1.1、下载ES压缩包 目前ElasticSearch最新的版本是7.6.2&#xff08;截止2020.4.1&#xff09;&#xff0c;我们选择6.8.1版本&#xff0c;建议使用JDK1.8及以上。 ElasticSearch分为Linux和Window版本&#xff0c;基于我们主要学习…

Linux服务器映射到本地磁盘

内容来自网友博客。 把linux服务器上的文件夹映射到本地作为一个磁盘来访问&#xff0c;步骤如下 一. samba的安装: sudo apt-get install samba // (sudo get temp root auth) sudo apt-get install smbfs //旧版本 sudo apt-get install cifs-utils //新版本 上…

【reactNative混合安卓开发~使用问题持续更】

reactNative混合安卓开发 reactNative开发移动端reactNative界面开发前端init.bat文件部分组件第三方组件解析1、定义theme主题shopify/restyle&#xff1b;菜单导航react-navigation/drawer、react-navigation/native&#xff1b; RN问题记录1、使用theme.js写的公共组件报错&…

【博客699】docker daemon预置iptables剖析

docker daemon预置iptables剖析 没有安装docker的机器&#xff1a;iptables为空&#xff0c;且每个链路的默认policy均为ACCEPT [root~]# iptables-save[root ~]# iptables -t raw -nvL Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)pkts bytes target prot opt …

k8s node 误删除了如何自动创建 csr重新加入集群

worker node 节点当部署晚 kubelet、kube-proxy就会加入集群&#xff0c;如何加入呢&#xff0c; [rootkube-node01 ssl]# mv kubelet-client-2023-08-13-01-19-00.pem kubelet-client-current.pem kubelet.crt kubelet.key /tmp/kubelet [rootkube-node01 ssl]# systemctl da…

Java 正则表达式【基本语法】

基本介绍 首先我们需要了解的是正则表达式中的各种元字符的功能&#xff0c;主要分为以下几种&#xff1a; 限定符选择匹配符分组组合和反向引用符特殊字符字符匹配符定位符 需要注意的是&#xff1a;在 Java正则表达式中&#xff0c;两个斜杠 \\ 相当于其它语言中的一个斜杠…

认识vite

一.了解vite的不同版本的更新 vite1版本是基于vue项目的&#xff0c;无法跨框架使用vite2可以跨框架&#xff08;vue2&#xff0c;vue3&#xff0c;react&#xff09;vite3模板变更&#xff1b;vite cli优化&#xff1b;import.meta.glob API变化&#xff1b;其他vite4主版本主…

Django框架-使用celery(一):django使用celery的通用配置,不受版本影响

目录 一、依赖包情况 二、项目目录结构 2.1、怎么将django的应用创建到apps包 三、celery的配置 2.1、celery_task/celery.py 2.2、celery_task/async_task.py 2.3、celery_task/scheduler_task.py 2.4、utils/check_task.py 四、apps/user中配置相关处理视图 4.1、基本…

【数据结构】复杂度

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;数据结构 &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、什么是数据结构 二、什么是算法 三、算法的效率 四、时间复杂度 4.…

k8s基础

k8s基础 文章目录 k8s基础一、k8s组件二、k8s组件作用1.master节点2.worker node节点 三、K8S创建Pod的工作流程&#xff1f;四、K8S资源对象1.Pod2.Pod控制器3.service && ingress 五、K8S资源配置信息六、K8s部署1.K8S二进制部署2.K8S kubeadm搭建 七、K8s网络八、K8…

人大金仓三大兼容:Oracle迁移无忧

企业级应用早期的架构模式是C/S&#xff08;Client/Server&#xff09;模式&#xff0c;Client做人机交互逻辑的呈现&#xff0c;Sever做业务计算逻辑的实现。这就类似餐馆的运作模式&#xff0c;Client是前台的服务员提供点菜和上菜服务&#xff0c;而Server则是后厨完成菜品的…

设计模式之工厂方法模式(FactoryMethod)

一、概述 定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。FactoryMethod使一个类的实例化延迟到其子类。 二、适用性 1.当一个类不知道它所必须创建的对象的类的时候。 2.当一个类希望由它的子类来指定它所创建的对象的时候。 3.当类将创建对象的职责委…

vue生命周期解析

Vue.js 是一个流行的前端 JavaScript 框架&#xff0c;它提供了一种用于构建用户界面的渐进式方法。Vue 组件的生命周期钩子函数是一些特定的函数&#xff0c;它们在组件不同阶段被自动调用&#xff0c;可以让你在这些阶段执行特定的逻辑。下面是 Vue 组件的生命周期钩子函数以…

【Spring】核心容器——Setter注入

1、引入 首先引入Spring框架依赖 <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.6.RELEASE</version> </dependency> 在dao层和service层分别准备两个接口以及他…