数据结构复习指导之交换排序(冒泡排序,快速排序)

目录

交换排序

复习提示

1.冒泡排序

1.1基本思想

1.2算法代码

1.3性能分析

2.快速排序

2.1基本思想

2.2算法代码

2.3性能分析


交换排序

复习提示

所谓交换,是指根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置。
基于交换的排序算法很多,本书主要介绍冒泡排序快速排序

其中冒泡排序算法比较简单,一般很少直接考查,通常会重点考查快速排序算法的相关内容

1.冒泡排序

1.1基本思想

冒泡排序的基本思想是:

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

图 8.3所示为冒泡排序的过程,

第一趟冒泡时:

  • 27<\overline{49},不交换;
  • 13<27,不交换;
  • 76>13,交换;
  • 97>13,交换;
  • 65>13,交换;
  • 38>13,交换;
  • 49>13,交换。

通过第一趟冒泡后,最小元素已交换到第一个位置,也是它的最终位置。

第二趟冒泡时对剩余子序列采用同样方法进行排序,如此重复,到第五趟结束后没有发生交换,说明表已有序,冒泡排序结束。

1.2算法代码

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

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

1.3性能分析

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

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


时间效率:当初始序列有序时,显然第一趟冒泡后 flag 依然为 false(本趟没有元素交换),从而直接跳出循环,比较次数为n-1,移动次数为0,

  • 从而最好情况下的时间复杂度为 O(n)
  • 当初始序列为逆序时,需要进行n-1趟排序,第 i 趟排序要进行n-i次关键字的比较,而且每次比较后都必须移动元素3次来交换元素位置。
  • 这种情况下, 
  • 比较次数=\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}
  • 从而,最坏情况下的时间复杂度为 O(n²),平均时间复杂度为 O(n²)

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


适用性:冒泡排序适用于顺序存储和链式存储的线性表

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

2.快速排序

2.1基本思想

快速排序(以下有时简称快排)的基本思想是基于分治法的:

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

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

命题追踪——快速排序的中间过程的分析(2014、2019、2023)】

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

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

用二叉树的形式描述这个举例的递归调用过程,如图 8.4所示。

假设划分算法已知,记为Partition(),返回的是上述的k,则 L(k) 已放在其最终位置。

2.2算法代码

因此可以先对表进行划分,然后对两个子表递归地调用快速排序算法进行排序。代码如下:

void QuickSort(ElemType 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+l,high);
    }
}

 【命题追踪——(算法题)快速排序中划分操作的应用(2016)】

快速排序算法的性能主要取决于划分操作的好坏。考研所考查的快速排序的划分操作通常总
以表中第一个元素作为枢轴来对表进行划分,则将表中比枢轴大的元素向右移动,将比枢轴小的
元素向左移动,使得一趟 partition()操作后,表中的元素被枢轴一分为二。代码如下:

int Partition(ElemType A[],int low,int high){ //一趟划分
    ElemType 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;          //返回存放枢轴的最终位置
}

2.3性能分析

快速排序算法的性能分析如下:
命题追踪——快速排序中递归次数的影响因素分析(2010)】

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

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

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

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

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

  • 适用性:快速排序仅适用于顺序存储的线性表

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

通过指针变量访问整型变量

有两个与指针变量有关的运算符&#xff1a; (1)&&#xff1a;取地址运算符。 (2)*&#xff1a;指针运算符&#xff08;或称间接访问运算符&#xff09;。 例如&#xff1a;&a为变量a的地址&#xff0c;*p为指针变量p所指向的存储单元。 编写程序&#xff1a; 运行结果…

第五十五周:文献阅读

目录 摘要 Abstract 文献阅读&#xff1a;基于VMD和深度学习的PM2.5浓度混合优化预测模型研究 一、现有问题 二、提出方法 三、方法论 1. 鲸优化算法&#xff08;WOA&#xff09; 2. 变分模式分解&#xff08;VMD&#xff09; 3.WOA-VMD优化方法 4. 双向长期记忆神经网…

V90PN伺服驱动器支持的标准报文介绍

1、V90 PN总线伺服通过FB285实现速度控制 V90 PN总线伺服通过FB285速度控制实现正弦位置轨迹运动(解析法和数值法对比测试)-CSDN博客文章浏览阅读448次。上面的位置函数有明确的解析函数&#xff0c;这里我们可以利用解析法求解其导数(微分),当然我们这里借助第三方数学软件求…

jrt落地deepin

经过昨天一晚上的努力&#xff0c;把deepin和win10的双系统安装好了。同时把jrt开发需要的svn&#xff0c;jdk,idea安装好里&#xff0c;代码也checkout里。 首先安装系统碰到安装deepin后启动时候无法选择win10,在宏伟兄帮助下找到资料执行sudo update-grub解决了。 然后程…

C++中的类

一&#xff0c;类的定义 class classname {//类体由成员函数和成员变量组成}; class为定义类的关键字&#xff0c;ClassName为类的名字&#xff0c;{}中为类的主体&#xff0c;注意类定义结束时后面分 号不能省略。 类的两种定义方式&#xff1a; 声明和定义全部放在类体中…

jmeter与loadrunner脚本生成最佳助手——fiddler

1、问题 现在好多系统使用IE访问会出现各种不支持问题&#xff0c;而loadrunner11录制脚本最好是使用IE。不然出现很多录制问题&#xff0c;如&#xff1a;loadrunner录制脚本为空的所有解决方法。badboy录制jmeter脚本也是会出现各种问题。   使用fiddler抓包&#xff0c;然…

如何恢复 Android 设备上丢失的照片

由于我们的大量数据和日常生活都存储在一台设备上&#xff0c;因此有时将所有照片本地存储在 Android 智能手机或平板电脑上可能是一种冒险行为。无论是由于意外&#xff08;损坏、无意删除&#xff09;&#xff0c;还是您认识的人翻看您的设备并故意删除了您想要保留的照片&am…

MySQL—函数(介绍)—字符串函数(基础)

一、引言 提到函数&#xff0c;在SQL分类中DQL语句中有一个聚合函数&#xff0c;如COUNT()、SUM()、MAX()等等。这些都是一些常见的聚合函数&#xff0c;而聚合函数只是函数的一种&#xff0c;接下来会详细的学习和介绍一下函数的应用场景和以及 mysql 当中文件的函数有哪些。 …

Unity DOTS技术(三)JobSystem+Burst+批处理

文章目录 一.传统方式二.使用JobSystemBurst方式三.批处理 在之前的例子中我们都中用的单线程与传统的编译器,下面我们试着使用JobSystem与打找Burst编译器来对比一下性能的差异. 一.传统方式 1.首先用传统方式创建10000个方块并让基每帧旋转 2.我们可以看到他的帧率是40 …

T检验——单样本t检验/两独立样本t检验/配对样本t检验

T检验——单样本t检验/两独立样本t检验/配对样本t检验 1.单样本t检验1.1 适用范围 2. &#xff08; 独立样本t检验&#xff09;两独立样本t检验3.ANOVA多组样本显著性检验&#xff08;2组以上&#xff09;4. 配对样本T检验 1.单样本t检验 1.1 适用范围 单样本t检验:即已知样本…

15 试用期,转正时我们要考察什么?

上一讲&#xff0c;我点出了“找人并不等于盲目加人”&#xff0c;你既要明确业务现状与团队需求&#xff0c;更要做好面试甄别&#xff0c;做出最优决定。那么当你找到人之后&#xff0c;是不是就可以高枕无忧了呢&#xff1f;并不是。 因为最终目的并非招聘&#xff0c;而是…

【Java数据结构】详解LinkedList与链表(四)

&#x1f512;文章目录&#xff1a; 1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; 2.什么是LinkedList 3.LinkedList的使用 3.1LinkedList的构造方法 3.2LinkedList的其他常用方法介绍 addAll方法 subList方法 LinkedList的常用方法总使…

[激光原理与应用-94]:电控 - 低噪声运放的原理

目录 一、什么是低噪声运放 1.1 什么是低噪声水平 1.2 什么是高增益 在电子工程中的应用 在通信领域的应用 在音频和视频处理中的应用 注意事项 1.3 什么是宽带宽 1.4 什么是低偏置电流 重要性 特点 解决方法 应用 二、低噪声运放的原理图 1. 基本构成 2. 设计…

Qml开发的两种方法

一.Qml开发的两种方法 1.Qt Creator 开发,手动编写qml代码 这种方法开发很方便&#xff0c;适合对qml语言非常熟悉的开发人员。 2.用Qt Design Studio 设计qml界面 这种方法更适合对qml不太熟悉的人&#xff0c;可以实现qml控件的拖拉拽&#xff0c;类似与widget界面开发&…

【面试经典150题】删除有序数组中的重复项

目录 一.删除有序数组中的重复项 一.删除有序数组中的重复项 题目如上图所示&#xff0c;这里非严格递增排序的定义是数字序列&#xff0c;其中相邻的数字可以相等&#xff0c;并且数字之间的差值为1。 这题我们依旧使用迭代器进行遍历&#xff0c;比较当前的数据是否与下一个数…

梯度下降: 01.原理与代码实操

1. 简介 梯度下降法(GradientDescent) 算法,不像多元线性回归那样是一个具体做回归任务的算法,而是一个非常通用的优化算法来帮助一些机器学习算法(都是无约束最优化问题)求解出最优解,所谓的通用就是很多机器学习算法都是用梯度下降,甚至深度学习也是用它来求解最优解。…

Android 控件保持宽高比得几种方式

文章目录 Android 控件保持宽高比得几种方式adjustViewBounds百分比布局ConstraintLayout自定义View Android 控件保持宽高比得几种方式 adjustViewBounds 仅适用于 ImageView&#xff0c;保持横竖比。 <ImageViewandroid:layout_width"match_parent"android:l…

0基础学习Elasticsearch-使用Java操作ES

文章目录 1 背景2 前言3 Java如何操作ES3.1 引入依赖3.2 依赖介绍3.3 隐藏依赖3.4 初始化客户端&#xff08;获取ES连接&#xff09;3.5 发送请求给ES 1 背景 上篇学习了0基础学习Elasticsearch-Quick start&#xff0c;随后本篇研究如何使用Java操作ES 2 前言 建议通篇阅读再回…

20240601在飞凌的OK3588-C开发板上跑IPC的SDK确认eth0

20240601在飞凌的OK3588-C开发板上跑IPC的SDK确认eth0 2024/6/1 14:04 结论&#xff1a;IPC因为需要推流&#xff0c;默认配置了DHCP&#xff0c;插网线可以自动获取IP地址&#xff1a;192.168.3.142 可以PING通局域网服务器&#xff1a;192.168.3.85和百度。 Buildroot默认只能…

Stable Diffusion安装记录II

文章目录 前言0 更改python路径&#xff08;跳过&#xff09;1 Torch is not able to use GPU1.1 确认显卡1.2 安装nvdia驱动 1.3 检查CUDA1.4更改启动脚本 2 依赖安装2.1 pip install报错2.2 git报错2.3 卡在installing requirements 3 启动咯~3.1 clip报错 4 成功运行4.1 遗留…