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

目录

交换排序

复习提示

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.rhkb.cn/news/338535.html

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

相关文章

ROS无人机追踪小车项目开发实战 | 第四届中国智能汽车创新大会圆满结束

2024年5月26日&#xff0c;阿木实验室在深圳第四届中国智能汽车创新大会上&#xff0c;开展的《Prometheus开源平台-ROS无人机追踪小车项目开发实战课》圆满结束。 该实战课从初学者的角度出发&#xff0c;通过实践性讲解和开发&#xff0c;使开发者们系统地学习了硬件系统架构…

vue3使用vue3-print-nb打印

打印效果 1.下载插件 Vue2.0版本安装方法 npm install vue-print-nb --saveVue3.0版本安装方法&#xff1a; npm install vue3-print-nb --save2.main.js引入 vue2引入 import Print from vue-print-nb Vue.use(Print)vue3引入 import print from vue3-print-nb // 打印…

在Windows安装Flutter

一、安装 Android Studio 官网&#xff1a; 下载 Android Studio 和应用工具 - Android 开发者 | Android Developers 教程&#xff1a;Android Studio 安装配置教程 - Windows(详细版)-CSDN博客 Flutter 官网&#xff1a;Windows | Flutter 中文文档 - Flutter 中文开发…

JVM学习-详解类加载器(一)

类加载器 类加载器是JVM执行类加载机制的前提 ClassLoader的作用 ClassLoader是Java的核心组件&#xff0c;所有的Class都是由ClassLoader进行加载的&#xff0c;ClassLoader负责通过各种方式将Class信息的二进制数据流读入JVM内部&#xff0c;转换为一个与目标类型对应的ja…

打开C语言常用的内存函数大门(三) —— memset()函数(内含讲解用法和模拟实现)

文章目录 1. 前言2. memset函数2.1 memset函数原型2.2 memset函数参数的介绍2.3 memset函数的使用演示 3. memset函数的模拟实现4. 总结 1. 前言 哈喽&#xff0c;我们又见面了。通过前面两个内存函数(memcpy、memmove函数)讲解的锤炼后&#xff0c;对如何解析一个自己从来没有…

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

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

【LIN】STM32新能源汽车LIN通信实现过程

【LIN】STM32新能源汽车LIN通信实现过程 文章目录 前言一、软件二、接线图三、硬件原理图四、上位机五、PICO示波器串行解码1.软件中的LIN波特率设置-192002.PIC设置3.PIC串行解码 六.引用总结 前言 【电机控制】直流有刷电机、无刷电机汇总——持续更新 使用工具&#xff1a;…

opencv笔记(13)—— 停车场车位识别

一、所需数据介绍 car1.h5 是训练后保存的模型 class_directionary 是0&#xff0c;1的分类 二、图像数据预处理 对输入图片进行过滤&#xff1a; def select_rgb_white_yellow(self,image): #过滤掉背景lower np.uint8([120, 120, 120])upper np.uint8([255, 255, 255])#…

单片机原理及应用复习

单片机原理及应用 第二章 在AT89S52单片机中&#xff0c;如果采用6MHz晶振&#xff0c;一个机器周期为 2us 。 时钟周期Tocs1focs 机器周期 Tcy12focs 指令周期&#xff1a;一条指令所用的时间&#xff0c;单字和双字节指令一般为单机器周期和双机器周期。 AT89S5…

Unity2D横版摄像机跟随

在Unity2D横版游戏中&#xff0c;摄像机跟随是一个非常重要的功能。一个流畅的摄像机跟随系统可以让玩家更好地沉浸在游戏世界中。本文将介绍如何在Unity中实现2D横版摄像机跟随&#xff0c;并分享一些优化技巧。 一、准备工作 在开始实现摄像机跟随之前&#xff0c;请确保您…

chatgpt之api的调用问题

1.调用api过程中&#xff0c;出现如下报错内容 先写一个测试样例 import openaiopenai.api_key "OPEN_AI_KEY" openai.api_base"OPEN_AI_BASE_URL" # 是否需要base根据自己所在地区和key情况进行completion openai.ChatCompletion.create(model"g…

python对文本操作,生成可执行文件

.exe文件主要包含pingmianF.py文件和read_inp_auto.py文件 实现效果 代码 read_inp_auto.py #-*- coding: utf-8 -*- import re import sys import os import os.path import time import pingmianF from pingmianF import vector import numpy as np from tkinter import me…

61. UE5 RPG 实现敌人近战攻击技能和转向攻击

在前面&#xff0c;我们实现了敌人的AI系统&#xff0c;敌人可以根据自身的职业进行匹配对应的攻击方式。比如近战战士会靠近目标后进行攻击然后躲避目标的攻击接着进行攻击。我们实现了敌人的AI行为&#xff0c;但是现在还没有实现需要释放的技能&#xff0c;接下来&#xff0…

VBA字典与数组第十五讲:多行多列数组与同列数单行数组间的运算规则

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…

开源模型应用落地-LangSmith试炼-入门初体验-监控和自动化(五)

一、前言 在许多应用程序中&#xff0c;特别是在大型语言模型(LLM)应用程序中&#xff0c;收集用户反馈以了解应用程序在实际场景中的表现是非常重要的。 LangSmith可以轻松地将用户反馈附加到跟踪数据中。通常最好提供一个简单的机制(如赞成和反对按钮)来收集用户对应用程序响…

Vue3中的常见组件通信之props和自定义事件

Vue3中的常见组件通信 概述 ​ 在vue3中常见的组件通信有props、mitt、v-model、 r e f s 、 refs、 refs、parent、provide、inject、pinia、slot等。不同的组件关系用不同的传递方式。常见的撘配形式如下表所示。 组件关系传递方式父传子1. props2. v-model3. $refs4. 默认…

【计算机毕业设计】331基于微信小程序的家庭财务管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Linux——多线程(一)

一、线程的概念 1.1线程概念 教材中的概念&#xff1a; (有问题?) 线程是进程内部的一个执行分支&#xff0c;线程是CPU调度的基本单位 之前我们讲的进程&#xff1a; 加载到内存中的程序&#x…

数据库与缓存⼀致性⽅案

数据库与缓存⼀致性⽅案 1、背景2、数据⼀致性⽅案设计3、数据⼀致性⽅案流程图4、关键代码4.1、 处理数据⼀致性的消息队列⼊⼝4.2、数据⼀致性配置的常量信息 1、背景 现有的业务场景下&#xff0c;都会涉及到数据库以及缓存双写的问题&#xff0c;⽆论是先删除缓存&#xf…

claude3国内API接口对接

众所周知&#xff0c;由于地理位置原因&#xff0c;Claude3不对国内开放&#xff0c;而国内的镜像网站使用又贵的离谱&#xff01; 因此&#xff0c;团队萌生了一个想法&#xff1a;为什么不创建一个一站式的平台&#xff0c;让用户能够通过单一的接口与多个模型交流呢&#x…