冒泡排序,快速排序详解及C++代码详细实现

2023-11-09

冒泡排序

冒泡排序的基本思想是:从后往前(或从前往后)两两比较相邻元素的值,若为逆序(即A[i-1]>A[i]),则交换它们,直到序列比较完。我们称它为第一趟冒泡,结果是将最小的元素交换到待排序列的第一个位置(或将最大的元素交换到待排序列的最后一个位置),关键字最小的元素如气泡一般逐渐往上“漂浮”直至“水面”(或关键字最大的元素如石头一般下沉至水底)。下一趟冒泡时,前一趟确定的最小元素不再参与比较,每趟冒泡的结果是把序列中的最小元素(或最大元素)放到了序列的最终位置……这样最多做 n − 1 n-1 n1 趟冒泡就能把所有元素排好序。

下图所示为冒泡排序的过程,第一趟冒泡时:27<49,不交换;13<27,不交换;76>13,交换;97>13,交换;65>13,交换;38>13,交换;49>13,交换。通过第一趟冒泡后,最小元素已交换到第一个位置,也是它的最终位置。第二趟冒泡时对剩余子序列采用同样方法进行排序,以此类推,到第五趟结束后没有发生交换,说明表已有序,冒泡排序结束。

image-20220813190439478
冒泡排序示例

冒泡排序算法的代码如下:

void BubbleSort(int *A, int n) {
    for (int i = 0; i < n - 1; i++) {
        int flag = false;   //表示本趟冒泡是否发生交换的标志
        for (int k = n - 1; k > i; k--)     //一趟冒泡过程
            if (A[k - 1] > A[k]) {      //若为逆序
                int temp = A[k - 1];    //交换
                A[k - 1] = A[k];
                A[k] = temp;
                flag = true;
            }
        if (flag == false)    //本趟遍历后没有发生交换,说明表已经有序
            return;
    }
}

冒泡排序的性能分析如下:

空间效率:仅使用了常数个辅助单元,因而空间复杂度为 O ( 1 ) O(1) O(1)

时间效率:当初始序列有序时,显然第一趟冒泡后flag依然为false(本趟冒泡没有元素交换),从而直接跳出循环,比较次数为 n − 1 n-1 n1 ,移动次数为0,从而最好情况下的时间复杂度为 O ( n ) O(n) O(n) ;当初始序列为逆序时,需要进行 n − 1 n-1 n1 趟排序,第 i i i 趟排序要进行 n − i n-i ni 次关键字的比较,而且每次比较后都必须移动元素3次来交换元素位置。这种情况下,
比较次数 = ∑ i = 1 n − 1 ( n − i ) = n ( n − 1 ) 2 ,移动次数 = ∑ i = 1 n − 1 3 ( n − i ) = 3 n ( n − 1 ) 2 比较次数=\sum_{i=1}^{n-1}(n-i) =\frac{n(n-1)}{2} ,移动次数=\sum_{i=1}^{n-1} 3(n-i) =\frac{3n(n-1)}{2} 比较次数=i=1n1(ni)=2n(n1),移动次数=i=1n13(ni)=23n(n1)
从而,最坏情况下的时间复杂度为 O ( n 2 ) O(n^2) O(n2) ,其平均时间复杂度也为 O ( n 2 ) O(n^2) O(n2)

稳定性:由于i>kA[i]=A[k]时,不会发生交换,因此冒泡排序是一种稳定的排序方法。

注意:冒泡排序中所产生的有序子序列一定是全局有序的(不同于直接插入排序),也就是说,有序子序列中的所有元素的关键字一定小于或大于无序子序列中所有元素的关键字,这样每趟排序都会将一个元素放置到其最终的位置上。

快速排序

快速排序的基本思想是基于分治法的:在待排序表 L[1...n]中任取一个元素pivot作为枢轴(或基准,通常取首元素),通过一趟排序将待排序表划分为独立的两部分L[1...k-1]L[k+1...n],使得L[1...k-1]中的所有元素小于pivotL[k+1...n]中的所有元素大于等于pivot,则pivot放在了其最终位置L(k)上,这个过程称为一趟快速排序(或一次划分)。然后分别递归地对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终位置上。

一趟快速排序的过程是一个交替搜索和交换的过程,下面通过实例来介绍,附设两个指针ij ,初值分别为lowhigh,取第一个元素49为枢轴赋值到变量pivot

指针jhigh往前搜索找到第一个小于枢轴的元素27,将27交换到i所指位置。

image-20220814112820798

指针ilow往后搜索找到第一个大于枢轴的元素65,将65交换到j所指位置。

image-20220814112928046

指针j继续往前搜索找到小于枢轴的元素13,将13交换到i所指位置。

image-20220814113108776

指针i继续往后搜索找到大于枢轴的元素97,将97交换到j所指位置。

image-20220814113630560

指针j继续往前搜索小于枢轴的元素,直至i==j

image-20220814113710055

此时,指针i (==j)之前的元素均小于49,指针i之后的元素均大于等于49,将49放在i所指位置即其最终位置,经过一趟划分,将原序列分割成了前后两个子序列。

image-20220814113932188

按照同样的方法对各子序列进行快速排序,若待排序列中只有一个元素,显然已有序。

image-20220814114038188

假设划分算法已知,记为Partition(),返回的是上述的k,注意到L(k)已在最终的位置,因此可以先对表进行划分,而后对两个表调用同样的排序操作。因此可以递归地调用快速排序算法进行排序,具体的程序结构如下:

void QuickSort(int *A, int low, int high) {
    if (low < high) {   //递归跳出的条件
        //Partition()就是划分操作,将表A[low...high]划分为满足上述条件的两个子表
        int pivotPos = Partition(A, low, high); //划分

        //依次对两个子表进行递归排序
        QuickSort(A, low, pivotPos - 1);
        QuickSort(A, pivotPos + 1, high);
    }
}

从上面的代码不难看出快速排序算法的关键在于划分操作,同时快速排序算法的性能也主要取决于划分操作的好坏。从快速排序算法提出至今,已有许多不同的划分操作版本,这里所讲的划分操作基本以严蔚敏的教材《数据结构》为主。假设每次总以当前表中第一个元素作为枢轴来对表进行划分,则将表中比枢轴大的元素向右移动,将比枢轴小的元素向左移动,使得一趟Partition()操作后,表中的元素被枢轴值一分为二。代码如下:

int Partition(int *A, int low, int high) {
    int pivot = A[low];     //将当前表中第一个元素设为枢轴,对表进行划分
    while (low < high) {
        while (low < high && A[high] >= pivot)
            high--;
        A[low] = A[high];   //将比枢轴小的元素移动到左端

        while (low < high && A[low] <= pivot)
            low++;
        A[high] = A[low];   //将比枢轴大的元素移动到右端
    }
    A[low] = pivot;   //枢轴元素存放到最终位置
    return low;       //返回存放枢轴的最终位置
}

快速排序算法的性能分析如下:

空间效率:由于快速排序是递归的,需要借助一个递归工作栈来保存每层递归调用的必要信息,其容量应与递归调用的最大深度一致。最好情况下为 O ( log ⁡ 2 n ) O(\log_{2}{n} ) O(log2n) ;最坏情况下,因为要进行 n − 1 n-1 n1 次递归调用,所以栈的深度为 O ( n ) O(n) O(n) ;平均情况下,栈的深度为 O ( log ⁡ 2 n ) O(\log_{2}{n} ) O(log2n)

时间效率:快速排序的运行时间与划分是否对称有关,快速排序的最坏情况发生在两个区域分别包含 n − 1 n-1 n1 个元素和0个元素时,这种最大限度的不对称性若发生在每层递归上,即对应于初始排序表基本有序或基本逆序时,就得到最坏情况下的时间复杂度为 O ( n 2 ) O(n^2) O(n2)

有很多方法可以提高算法的效率:一种方法是尽量选取一个可以将数据中分的枢轴元素,如从序列的头尾及中间选取三个元素,再取这三个元素的中间值作为最终的枢轴元素;或者随机地从当前表中选取枢轴元素,这样做可使得最坏情况在实际排序中几乎不会发生。

在最理想的状态下,即Partition()可能做到最平衡的划分,得到的两个子问题的大小都不可能大于 n / 2 n/2 n/2 ,在这种情况下,快速排序的运行速度将大大提升,此时,时间复杂度为 O ( n log ⁡ 2 n ) O(n\log_{2}{n} ) O(nlog2n) 。好在快速排序平均情况下的运行时间与其最佳情况下的运行时间很接近,而不是接近其最坏情况下的运行时间。快速排序是所有内部排序算法中平均性能最优的排序算法

稳定性:在划分算法中,若右端区间有两个关键字相同,且均小于基准值的记录,则在交换到左端区间后,它们的相对位置会发生变化,即快速排序是一种不稳定的排序方法。例如,表L= {3, 2, 2},经过一趟排序后L= {2, 2, 3},最终排序序列也是L= {2, 2, 3},显然,2与2的相对次序已发生了变化。

注意:在快速排序算法中,并不产生有序子序列,但每趟排序后会将枢轴(基准)元素放到其最终的位置上。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

冒泡排序,快速排序详解及C++代码详细实现 的相关文章

  • 如何在c++中读取pcap文件来获取数据包信息?

    我想用 C 编写一个程序来读取 pcap 文件并获取数据包的信息 例如 len sourc ip flags 等 现在我找到了如下代码 我认为它会帮助我获取信息 但是我有一些疑问 首先我想知道应该将哪个库添加到我的程序中 然后什么是 pca
  • 当我们想要返回对象的引用时,为什么我们在赋值运算符中返回 *this 而通常(而不是 this)?

    我正在学习 C 和指针 我以为我理解了指针 直到我看到这个 一方面 asterix 运算符是解引用的 这意味着它返回值所指向的地址中的值 而与号 运算符则相反 它返回值存储的地址记忆 现在阅读有关赋值重载的内 容 它说 我们返回 this因
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 由 IHttpClientFactory 注入时模拟 HttpClient 处理程序

    我创建了一个自定义库 它会自动为依赖于特定服务的 Polly 策略设置HttpClient 这是使用以下方法完成的IServiceCollection扩展方法和类型化客户端方法 一个简化的例子 public static IHttpClie
  • 将 Word 文档另存为图像

    我正在使用下面的代码将 Word 文档转换为图像文件 但是图片显得太大 内容不适合 有没有办法渲染图片或将图片保存到合适的尺寸 private void btnConvert Click object sender EventArgs e
  • 在 C 中初始化变量

    我知道有时如果你不初始化int 如果打印整数 您将得到一个随机数 但将所有内容初始化为零似乎有点愚蠢 我问这个问题是因为我正在评论我的 C 项目 而且我对缩进非常直接 并且它可以完全编译 90 90 谢谢 Stackoverflow 但我想
  • 是否有实用的理由使用“if (0 == p)”而不是“if (!p)”?

    我倾向于使用逻辑非运算符来编写 if 语句 if p some code 我周围的一些人倾向于使用显式比较 因此代码如下所示 if FOO p some code 其中 FOO 是其中之一false FALSE 0 0 0 NULL etc
  • 标准化 UTF-8 到底是什么?

    The 重症监护室项目 http userguide icu project org transforms normalization 现在也有一个PHP库 http us php net manual en class normalize
  • 在一个平台上,对于所有数据类型,所有数据指针的大小是否相同? [复制]

    这个问题在这里已经有答案了 Are char int long 甚至long long 大小相同 在给定平台上 不能保证它们的大小相同 尽管在我有使用经验的平台上它们通常是相同的 C 2011 在线草稿 http www open std
  • 如何在 32 位或 64 位配置中以编程方式运行任何 CPU .NET 可执行文件?

    我有一个可在 32 位和 64 位处理器上运行的 C 应用程序 我试图枚举给定系统上所有进程的模块 当尝试从 64 位应用程序枚举 32 位进程模块时 这会出现问题 Windows 或 NET 禁止它 我认为如果我可以从应用程序内部重新启动
  • 如何在 Xaml 文本中添加电子邮件链接?

    我在 Windows Phone 8 应用程序中有一些大文本 我希望其中有电子邮件链接 例如 mailto 功能 这是代码的一部分
  • 使用自定义堆的类似 malloc 的函数

    如果我希望使用自定义预分配堆构造类似 malloc 的功能 那么 C 中最好的方法是什么 我的具体问题是 我有一个可映射 类似内存 的设备 已将其放入我的地址空间中 但我需要获得一种更灵活的方式来使用该内存来存储将随着时间的推移分配和释放的
  • Cmake 链接共享库:包含库中的头文件时“没有这样的文件或目录”

    我正在学习使用 CMake 构建库 构建库的代码结构如下 include Test hpp ITest hpp interface src Test cpp ITest cpp 在 CMakeLists txt 中 我用来构建库的句子是 f
  • 如何在非控制台应用程序中查看 cout 输出?

    输出到调试窗口似乎相当繁琐 我在哪里可以找到cout如果我正在编写非控制台信息 则输出 Like double i a b cout lt lt b lt lt endl I want to check out whether b is z
  • 使用 %d 打印 unsigned long long

    为什么我打印以下内容时得到 1 unsigned long long int largestIntegerInC 18446744073709551615LL printf largestIntegerInC d n largestInte
  • 按 Esc 按键关闭 Ajax Modal 弹出窗口

    我已经使用 Ajax 显示了一个面板弹出窗口 我要做的是当用户按 Esc 键时关闭该窗口 这可能吗 如果有人知道这一点或以前做过这一点 请帮助我 Thanks 通过以下链接 您可以通过按退出按钮轻松关闭窗口 http www codepro
  • 调用堆栈中的“外部代码”是什么意思?

    我在 Visual Studio 中调用一个方法 并尝试通过检查调用堆栈来调试它 其中一些行标记为 外部代码 这到底是什么意思 方法来自 dll已被处决 外部代码 意味着该dll没有可用的调试信息 你能做的就是在Call Stack窗口中单
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • 方法优化 - C#

    我开发了一种方法 允许我通过参数传入表 字符串 列数组 字符串 和值数组 对象 然后使用这些参数创建参数化查询 虽然它工作得很好 但代码的长度以及多个 for 循环散发出一种代码味道 特别是我觉得我用来在列和值之间插入逗号的方法可以用不同的
  • 使用 .NET Process.Start 运行时挂起进程 - 出了什么问题?

    我在 svn exe 周围编写了一个快速而肮脏的包装器来检索一些内容并对其执行某些操作 但对于某些输入 它偶尔会重复挂起并且无法完成 例如 一个调用是 svn list svn list http myserver 84 svn Docum

随机推荐

  • lammps基础命令及教程

    原创 YJ学长 LAMMPS交流站javascript void 0 01 lammp常用命令 1 units命令 2 dimension命令 3 boundary命令 3 atom style命令 4 neighbor命令 5 neigh
  • Matplotlib之散点图绘制

    文章目录 1 散点图简介 2 散点图的应用场景 3 绘制散点图 4 回归分析 1 散点图简介 散点图也叫 X Y 图 它将所有的数据以点的形式展现在直角坐标系上 以显示变量之间的相互影响程度 点的位置由变量的数值决定 通过观察散点图上数据点
  • python 去除列表重复元素

    1 1逻辑去除 推荐面试使用 def dedup list li 定义一个列表去重的函数 定义一个空列表用于接收不重复的列表元素 dedup li list 定义一个相关变量 用于下标的判断 index 0 相同的任意个元素 用列表的ind
  • js压缩图片

    1 压缩方法 图片压缩方法 imageHandel imageCompress 若按照指定大小压缩则quality参数无效 按照图片大小压缩会存在误差 author luxuebo Date 2020 04 04 param file Fi
  • OpenStack--部署认证服务keystone

    官方安装文档 https docs openstack org ocata zh CN install guide rdo index html 1 keystone数据库配置 1 创建数据库 root linux host4 mysql
  • pytorch转onnx(支持动态batchsize、shape

    以fcos模型为例 需要输出fpn的5个feature map 需要支持多个尺寸输出 不同batchsize 1 转onnx模型 import argparse import os path as osp import warnings i
  • GPS设备获取的坐标转换成百度或者高德坐标

    JSON文件数据格式如上 用底下的转换工具类即可完成转换 直接上代码 import com fasterxml jackson databind ObjectMapper import com zc smartcity ZtfServerA
  • org.springframework.data.repository.query.QueryByExampleExecutor cannot be resolved.

    SpringBoot 集成JPA提示如下错误信息 The type org springframework data repository query QueryByExampleExecutor cannot be resolved It
  • 多团队协作开发的大型项目Git工作流设计分享

    一 项目简介 文章内容以我自己实际负责的项目前端代码的管理为例 每个公司的git工作流设计应以公司的实际为准 该分享仅做参考 1 项目架构设计 采用基于qiankun的前端微应用 基座应用 业务模块应用 架构设计 项目所管理的供应商达400
  • 一些在前后端用来进行储存数据的地方或者方式

    一些在前后端用来进行储存数据的地方或者方式 文件 可以通过生成新文件的方式将新数据进行储存 这里要考虑到对文件的读写 容量 操作繁琐等等 数据库 属于后端储存数据的地方 如果前端去拿数据的话需要发送HTTP请求 缓存 也叫cache mem
  • VUE tree树双击选中(显示层级结构) 及 vuedraggable插件拖拽功能的实现

    左侧双击选中到中间 显示层级结构 中间可拖拽到右侧 且不可重复拖拽 右侧上下可互相拖拽
  • JS 删除数组中某个元素的几种方式

    目录 第一种 删除最后一个元素 pop 删除 slice 删除 splice 删除 for 删除 length 删除 第二种 删除第一个元素 shift 删除 slice 删除 splice 删除 第三种 删除数组中某个指定下标的元素 sp
  • Opencascade之选择对象

    一 选择模式 Opencascade 通过鼠标选择对象 有多种选择模式 调用AIS InteractiveContext Activate方法进行设置 void SinnView SetSelectMode TopAbs ShapeEnum
  • Vue组件通信方式(8种)

    1 一图认清组件关系名词 父子关系 A与B A与C B与D C与E 兄弟关系 B与C 隔代关系 A与D A与E 非直系亲属 D与E 总结为三大类 父子组件之间通信 兄弟组件之间通信 跨级通信 2 8种通信方式及使用总结 props emit
  • Wireshark—网络分析工具

    Wireshark介绍 WireShark是非常流行的网络封包分析工具 可以截取各种网络数据包 并显示数据包详细信息 常用于开发测试过程中各种问题定位 WireShark软件安装 软件下载路径 wireshark官网 按照系统版本选择下载
  • 电子设计大赛应该准备什么

    电赛的准备 电子设计大赛应该准备什么 基 础 知 识 储 备 基本材料的准备 必 备 技 能 项 目 训 练 Wish 总结 电子设计大赛应该准备什么 2021年的电子设计大赛就要来了 小伙伴是否已经开始紧张的装备呢 下面进入正题 想参加比
  • 记录一次笔试题(R语言)

    记录一次笔试题 R语言 data lt read csv 银行 csv 1 取出李姓 法1 record xingshi c FALSE FALSE FALSE FALSE for i in 1 4 if substring data i
  • mybatis将时间存入数据库后,只有日期,时分秒全是0

    问题原因分析 a 数据库字段类型问题 mysql数据库中 date 为年月日 time为时分秒 datetime为年月日时分秒 pgsql数据库中 Date为年月日 timestamp为年月日时分秒 b mybatis中jdbcType c
  • 【数据分析】数据分析方法(四):多维度拆解分析 & 对比分析

    数据分析方法 四 多维度拆解分析 对比分析 1 多维度拆解分析方法 对于多维度拆解分析方法 要理解两个关键词 维度 拆解 只看数据整体 我们可能注意不到数据内部各个部分构成的差异 如果忽略这种差异进行比较 就有可能导致无法察觉该差异所造成的
  • 冒泡排序,快速排序详解及C++代码详细实现

    冒泡排序 冒泡排序的基本思想是 从后往前 或从前往后 两两比较相邻元素的值 若为逆序 即A i 1 gt A i 则交换它们 直到序列比较完 我们称它为第一趟冒泡 结果是将最小的元素交换到待排序列的第一个位置 或将最大的元素交换到待排序列的