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

目录

交换排序

复习提示

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的相对次序已发生了变化。

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

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

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

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

相关文章

链表逆序用哨兵位头节点

在C语言中实现链表的逆序&#xff0c;使用哨兵头节点是一种常见的做法。哨兵头节点可以简化代码逻辑&#xff0c;特别是当链表为空时&#xff0c;可以避免空指针异常。下面是一个使用哨兵头节点逆序单链表的C语言实现 示例&#xff1a; #include <stdio.h> #include &l…

富格林:应用正规技巧阻挠被骗

富格林悉知&#xff0c;随着如今入市现货黄金的朋友愈来愈多&#xff0c;不少投资者也慢慢开始重视起提高自身的正规投资技巧&#xff0c;希望能阻挠被骗更高效地在市场上获利。虽然目前黄金市场存在一定的受害风险&#xff0c;但只要投资者严格按照正规的交易规则来做单&#…

python解决flask启动的同时启动定时任务

业务场景描述&#xff1a;在常规的开发中&#xff0c;我们开发接口服务&#xff0c;一般会将数据放在数据库、文件等第三方文件&#xff0c;启动服务后&#xff0c;服务到后台数据库中加载数据&#xff0c;这样做的好处当然是开发会更加便利以及数据的可复用性较高&#xff0c;…

深度学习-03-函数的连续调用

深度学习-03-函数的连续调用 本文是《深度学习入门2-自製框架》 的学习笔记&#xff0c;记录自己学习心得&#xff0c;以及对重点知识的理解。如果内容对你有帮助&#xff0c;请支持正版&#xff0c;去购买正版书籍&#xff0c;支持正版书籍不仅是尊重作者的辛勤劳动&#xff0…

LLaMA-Factory推理实践

运行成功的记录 平台&#xff1a;带有GPU的服务器 运行的命令 git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory/ conda create -n py310 python3.10 conda activate py310由于服务器不能直接从huggingface上下载Qwen1.5-0.5B&#xff0c;但本地可…

51仿真器 PZ-51Tracker 未知设备

插上仿真器&#xff0c;右击我的电脑 等待一下&#xff0c;选择winUSB 此时在keil中选择仿真器会报错&#xff0c;需要安装如下我是win10) 安装好后退出再试&#xff0c;没有报错即可 这项也要选择 另外配置晶振

MYSQL之存储篇

MYSQL之存储篇 存储过程简介存储过程优点&#xff1a; MySQL的存储过程MySQL存储过程的创建1.格式2.声明分割符3.参数4.变量5.注释6.MySQL存储过程的调用7. MySQL存储过程的查询8.MySQL存储过程的修改9.MySQL存储过程的删除10. MySQL存储过程的控制语句11.MySQL存储过程的基本函…

mybatis配置环境流程

mybatis配置环境流程 为啥要用mybatis&#xff1a;通过Mybatis实现快速访问后端pgsql、mysql等数据库。 1.修改pom.xml&#xff0c;添加mybatis相关依赖 <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-s…

React + SpringBoot开发用户中心管理系统

用户中心项目搭建笔记 技术栈 前端技术栈 “react”: “^18.2.0”,ant-design-pro 后端技术栈 SpringBoot 2.6.x 项目源码地址 https://gitee.com/szxio/user-center 前端项目搭建 快速搭建一个后端管理系统项目框架 初始化 antDesignPro 官网&#xff1a; https://…

Spel表达式使用案例

package com.example.demo.api;import com.example.demo.model.User; import lombok.extern.slf4j.Slf4j;<

CSS Web前端框架:深入剖析与应用实践

CSS Web前端框架&#xff1a;深入剖析与应用实践 在快速发展的Web技术领域&#xff0c;CSS Web前端框架已成为构建现代化、高效且响应式网页的关键工具。它们不仅简化了开发过程&#xff0c;还提高了代码的可维护性和复用性。然而&#xff0c;面对众多纷繁复杂的框架选择&…

ByteBuddy字节码增强器

Byte Buddy是java的字节码增强器&#xff0c;一个优雅的运行时java代码生成库&#xff0c;使用时需要慎重 文档地址&#xff1a;http://bytebuddy.net/#/tutorial-cn 1. 引入ByteBuddy <!-- https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy --><depend…

LeetCode---哈希表

242. 有效的字母异位词 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 t 中每个字符出现的次数都相同&#xff0c;则称 s 和 t 互为字母异位词。 代码示例&#xff1a; //时间复杂度: O(n) //空间复杂度: O(1) c…

Java生成PDF笔记整理

引入依赖, groupId:com.itextpdf, version:8.0.4, artifactId如下kernel,io,layout,forms创建pdf对象try(ByteArrayOutputStream outputStream new ByteArrayOutputStream()){PdfWriter writer new PdfWriter(outputStream, new WriterProperties().setFullCompressionMode(t…

离线安装python库

1. 下载安装包 在联网机器上安装 # 选择符合目标架构的版本&#xff0c;主要是libc版本和python版本 pip download --platformmanylinux2010_x86_64 --only-binary:all: --python-version3.7.4 tabulate # 或者 pip download --platformmanylinux_2_5_x86_64 --only-binary:a…

do...while循环

基本语法 while循环&#xff0c;是先判断条件再执行。 do...while循环&#xff0c;是先斩后奏&#xff0c;先至少执行一次循环语句块中的逻辑&#xff0c;再判断是否继续。 do {//do while 循环语句块; } while (bool类型的值);注意&#xff1a;do...while语句&#xff0c;存…

Common Lisp笔记

在计划学习函数式编程的时候&#xff0c;我一开始打算学习的是 F#。因为我朋友就是在 DTU 上的学&#xff0c;F# 就是 DTU&#xff08;丹麦理工&#xff09;开发的。但是由于 F# 和微软的 .NET 绑定&#xff0c;而在 macOS 上&#xff0c;目前版本的 .NET 的是有些问题的&#…

2020编程语言排序:探索编程界的热门与趋势

2020编程语言排序&#xff1a;探索编程界的热门与趋势 在数字时代的浪潮中&#xff0c;编程语言作为构建数字世界的基石&#xff0c;其流行度和影响力不容忽视。2020年&#xff0c;各大编程语言在各自的领域里展现出独特的魅力和实力。本文将从四个方面、五个方面、六个方面和…

线性代数|机器学习-P3乘法和因式分解矩阵

文章目录 1. 矩阵分解2. S Q Λ Q T SQ\Lambda Q^T SQΛQT3. A U Σ V T AU\Sigma V^T AUΣVT4. A LU 分解5. 矩阵的四个子空间 1. 矩阵分解 目前我们有很多重要的矩阵分解&#xff0c;每个分解对应于多个前提条件&#xff0c;分解方法&#xff0c;分解后的形状会中如下&…

【Vue】v-for中的key

文章目录 一、引入问题二、分析问题 一、引入问题 语法&#xff1a; key属性 "唯一值" 作用&#xff1a;给列表项添加的唯一标识。便于Vue进行列表项的正确排序复用。 为什么加key&#xff1a;Vue 的默认行为会尝试原地修改元素&#xff08;就地复用&#xff09;…