数据结构与算法基础(王卓)(36):交换排序之快排【第三阶段:深挖解决问题】精华!精华!精华!!!重要的事情说三遍

news2024/11/15 17:54:17

目录

Review:

具体问题:

操作核心:

注:

操作分解:

操作实现:

问题(1):进行不一样次数的 if / else 判断

问题(2):通过判断条件为“小于哨兵的元素”行不通

问题(3):

当采用【大于等于】作为循环条件(while)循环时候程序的运行原理(框架)

一开始

后来我们发现

你以为到这里我们终于要结束了吗?

然而并没有

最大的问题:

一开始(前面),我们以为:

而在后来我们又发现,实际上并没有那么简单:

真正的问题:

将问题在程序流程中具体化:

(1):

(2):

具体化的完整过程:

(1):

(2):

没有确认找到下一个放入空格的元素再进行下一步(换指针操作)

而是只要对下一个元素进行过了比较、进行了交换元素或移动该位置指针的操作,就进行下一步

修改最终结果如下:

我们只有在确定找到了比哨兵大/小的元素、确定进行了对空格的插入操作以后我们才换指针操作

而前面的程序不管有没有找到、交不交换,都进行换指针的操作

其实我们要修改的结果就是要让程序确定执行了插入空格的操作再进行下一步(下一轮)

标准答案:


Review:

具体问题:

运行逻辑机制问题,我们就拿PPT上的实例来说事吧:

将逐次具体操作转化为表格展示如下: 

第几步操作Low指向High指向
第1步49变哨兵18
第2步

49'不动,high--(第一个 if / else 判断)

17
第3步49不动,low++(第二个 if / else 判断)27


这里到了第三步,明显就开始出问题了:

空格里面还没有元素呢,你这个算法就开始超过他、跳跃到下一格,干什么?

重复比较无伤大雅,但是low移动了,危险!!!(要出错了)

这里很明显,我们可以看到,问题出在:

在第一个处理的元素被放到哨兵里后,我们又去拿这个元素本身比较哨兵

也就是自己比较自己,错过了这个空格;

而不是用这个空格来装第一个小于哨兵的元素


操作核心:

所以我们需要操作的就是:

从程序开头开始,一直监视这个程序运行操作的每一步,确保:

我们【第一个元素让出来的空格】装入【第一个小于哨兵的元素】


注:

监视:

更准确的来说,这里我们这个所谓的“监视”,指的是是重新手写一遍

从程序开头开始,直到【第一个元素让出来的空格】装入【第一个小于哨兵的元素】

这段过程,整个过程的所有操作的程序,全部重新自己一步一步手写一遍


操作分解:

而我们要(在一开始)手写的:

从程序开头开始,直到【第一个元素让出来的空格】装入【第一个小于哨兵的元素】

的整个过程的操作,也无非就是:

从程序开头开始,(一直)比较high指针:(先比较最后一位)

  1. 若【high指针指向元素】小于哨兵,就把元素放前面空格里面
  2. 若不是小于(>=):
  • 这个元素继续放后面
  • 【high指针】继续往前寻找,比较前面一个元素和哨兵的大小

操作实现:

问题(1):进行不一样次数的 if / else 判断

而这里,如果我们写程序还是跟着/像上一节那样,每次都简单的只是采用 if / else 判断语句,那么

我们对于不同的顺序表:

无疑每一次 都要进行不一样次数的 if / else 判断

(谁知道后面第一个小于哨兵的元素的位置有多前面)

这(样)无疑是不行的


问题(2):通过判断条件为“小于哨兵的元素”行不通

既然我们的目的是要找到从后往前数第一个小于哨兵的元素

直接找到这个“第一个小于哨兵的元素”本身

如果设置条件为:通过【判断条件为“小于哨兵的元素”】直接查找的话

除非最后一个元素就小于哨兵,要不然根本不可能直接找到

如果有大于等于哨兵的元素我们根本跨不过去

if (low < high && L.r[high].key >= L.r[0].key)
{
    if (low < high && L.r[high].key >= L.r[0].key)
    {
        ...//无数个:
//【if (low < high && L.r[high].key >= L.r[0].key){}     else  把元素放前面空格里面】语句
//根本写不完,实现不了,死循环
    }
    else  把元素放前面空格里面
}
else  把元素放前面空格里面

所以,我们只能设置把判断循环条件改为不是小于(>=)哨兵的情况


问题(3):

当采用【大于等于】作为循环条件(while)循环时候程序的运行原理(框架)


一开始

我们在写算法的时候想当然的以为

用【大于等于】作为循环条件(while)循环的时候,策略是退而求其次

先确定找到【小于(哨兵元素)的前面一个元素】;

然后再通过小于哨兵的元素的前面一个元素通过(+1)的方式找到该元素进行操作


后来我们发现

不对,上面写这个执行流程是我们想当然的结果

实际上,把循环条件改为【大于等于】哨兵时,程序运行的逻辑是:

如果大于等于:一直high--;

直到我们找到第一个小于哨兵的元素

程序退出循环时,high已经指向了交换时我们所需要指向的元素

直接找到了这个“第一个小于哨兵的元素”本身

而不是上一个元素

于是做出修改: 

int 遍历(SqList &L, int low, int high)
{
    L.r[0] = L.r[low];
    while (low < high && L.r[high].key >= L.r[0].key)
        high--;
    L.r[low] = L.r[high];


    while (low < high)
    {
        if (L.r[high].key < L.r[0].key)
        {
            L.r[low] = L.r[high];
            low++;
        }
        else
            high--;
        if (L.r[0].key < L.r[low].key)
        {
            L.r[high] = L.r[low];
            high--;
        }
        else
            low++;
    }
    L.r[low] = L.r[high] = L.r[0];
    return low;
}

void QuickSort(SqList& L, int low, int high)
{
    int pivot = 遍历(L, low, high);
    QuickSort(L, low, pivot-1);
    QuickSort(L, pivot + 1, high);
}

int main()
{
    SqList L;
    cin >> L.length;
    cin >> L.r->key;

    QuickSort(L, 1, L.length);
}

你以为到这里我们终于要结束了吗?

然而并没有


最大的问题:

然后,我们又继续深挖下去,发现这个程序的问题并没有那么简单:

一开始(前面),我们以为:

程序只因为在开头,由于第一个元素存入哨兵而出现了:

元素本身比较哨兵,也就是自己比较自己,错过空格;

的现象而产生的错误,重要的是:

我们以为这里是一个特殊情况,整段程序只有开头出现了问题

而在后来我们又发现,实际上并没有那么简单:

程序存在的在开头已经出现的(无法正确排序)问题,在后面后续(的)程序中同样存在:

真正的问题:

如果前/后面的空格还没补上,我们就开始移动前/后面的指针

那么这个空格就永远都不会再被填上了,于是(从此),我们就再也不能回去找不到这个空格了

然后后面的程序全部乱套,出大问题


将问题在程序流程中具体化:

(程序)真正(出现)的问题在于:

在还没有确定有:

(1):

high指针指向的元素】已经移动(填充)到前面的空格中

我们就开始移动low指针,指向空格的后面一个元素

或者

(2):

【low指针指向的元素】已经移动(填充)到后面的空格中

我们就开始移动high指针,指向空格的前面一个元素


具体化的完整过程:


(1):

在还没有确定有:

high指针指向的元素】已经移动(填充)到前面的空格中

我们就开始移动low指针,指向空格的后面一个元素


当开始(新一轮)开头比较时(从第二个if/else语句开始)

如果我们碰到的情况是:high指针指向的元素并不比哨兵小

(L.r[high].key < L.r[0].key)不成立,所以执行else语句:high--;

执行第二个if/else语句:

到这里一切都还没什么问题,然后下一步就出问题了:

根据我们手动操作的流程,程序下一步本来应该继续执行“high--;”

但是在Project 1中,根据 Project 1的程序的操作流程,下一步的操作就是:

  • 若low指向的首个元素大于哨兵:L.r[high] = L.r[low];high--;

    直接把high指针元素放进空格,此种情况除非倒数第二个元素恰好小于哨兵,否则也是错误的插入


  • 若low指向的首个元素小于等于哨兵,low++;

    直接移动low指针,再也找不到空格


(2):

在还没有确定有:

【low指针指向的元素】已经移动(填充)到后面的空格中

我们就开始移动high指针,指向空格的前面一个元素


当开始(新一轮)开头比较时(从第二个if/else语句开始)

如果我们碰到的情况是:low指针指向的元素并不比哨兵大
 
(L.r[0].key < L.r[low].key)不成立,所以执行else语句:low++;

重新执行第一个if/else语句


若high指向的首个元素小于哨兵:L.r[low] = L.r[high];low++;

直接把low指针元素放进空格,此种情况除非第二个元素恰好大于哨兵,否则也是错误的插入

若high指向的首个元素大于等于哨兵, high--;

直接移动high指针,再也找不到空格


说到底,程序这个问题的核心,就是程序执行的操作:

没有确认找到下一个放入空格的元素再进行下一步(换指针操作)

而是只要对下一个元素进行过了比较、进行了交换元素或移动该位置指针的操作,就进行下一步

修改最终结果如下:

#include<iostream>
using namespace std;

#define MAXSIZE 20  //记录最大个数
typedef int KeyType;  //关键字类型

typedef int InfoType;

//定义每个记录(数据元素)的结构
struct RecType
    //Record Type:每条记录的类型
{
    KeyType key;  //关键字
    InfoType otherinfo;  //其他数据项
};

struct SqList
    //顺序表(的)结构
{
    RecType r[MAXSIZE + 1];
    //类型为【记录类型】的数组
    //r[0]一般做哨兵或缓冲区
    int length;  //顺序表长度
};

int 遍历(SqList& L, int low, int high)
{
    L.r[0] = L.r[low];
    while (low < high)
    {
        //从后往前遍历,指向小于等于哨兵的元素:退出循环、插入空格
        while (low < high && L.r[high].key > L.r[0].key)         
            high--;
        L.r[low] = L.r[high];

        //继续,从前往后遍历,指向大于等于哨兵的元素:退出循环、插入空格
        while (low < high && L.r[0].key < L.r[low].key)
            low++;
        L.r[high] = L.r[low];
    }
    L.r[low] = L.r[high] = L.r[0];
    return low;
}

void QuickSort(SqList& L, int low, int high)
{
    int pivot = 遍历(L, low, high);
    QuickSort(L, low, pivot - 1);
    QuickSort(L, pivot + 1, high);
}

int main()
{
    SqList L;
    cin >> L.length;
    cin >> L.r->key;

    QuickSort(L, 1, L.length);
}

与前面我们写的程序不同的是:

我们只有在确定找到了比哨兵大/小的元素、确定进行了对空格的插入操作以后我们才换指针操作

而前面的程序不管有没有找到、交不交换,都进行换指针的操作

其实我们要修改的结果就是要让程序确定执行了插入空格的操作再进行下一步(下一轮)


当然,这样修改以后和标准答案还是有区别的,但不多:

标准答案:

int Partition(SqList& L, int low, int high)
{
    L.r[0] = L.r[low];
    KeyType pivotkey = L.r[low].key;
    while (low < high) 
    {
        while (low < high && L.r[high].key >= pivotkey)
            high--;  
        L.r[low] = L.r[high];

        while (low < high && L.r[low].key < pivotkey)
            low++;
        L.r[high] = L.r[low];
    }
    L.r[low] = L.r[0];
    return low;
}

void QuickSort(SqList& L, int low, int high) {
    if (low < high)
    {
        int pivotloc = Partition(L, low, high);  //将L一份为二
        QuickSort(L, low, pivotloc - 1);  //对低子表递归排序
        QuickSort(L, pivotloc + 1, high);  //对高子表递归排序
    }
}

int main()
{

}

唯一的区别就是标准答案为了这个比较的节点还特地设立了一个变量 pivotkey

我感觉其实无所吊味,没什么卵用啦,还不如老子写的这个简单方便

就这样吧,Cnmd,写这篇文章搞这个快排折腾了老子至少一礼拜至少5天的时间,我是真NM无语

以上

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

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

相关文章

Element-UI

目录 Layout 布局 按钮组件结合 el-icon 使用 单选框 复选框 日期组件 表格 分页 对话框 表单验证 Element-UI是由饿了么前端团队开发的一套基于Vue.js的桌面端组件库&#xff0c;包含了多个常用的UI组件&#xff0c;如按钮、输入框、表格、弹窗等&#xff0c;可以快速…

5.QT应用程序主窗口

本章代码见文末链接 主窗口框架 新建Qt Wisgets项目mymainwindow&#xff0c;类名默认MainWindow&#xff0c;基类默认QMainWindow 更改文字如图&#xff0c;如果中文无法直接输入&#xff0c;可以试试复制粘贴 “动作编辑器”中&#xff08;默认在右下角&#xff09;&…

AI数字人系统搭建源码

AI数字人系统的功能可以根据具体应用场景而定&#xff0c;以下是一些可能的功能&#xff1a; 语音识别和合成&#xff1a;将自然语言转换为机器可读的文本&#xff0c;或将机器生成的文本转换为自然语言的语音输出。 面部表情捕捉&#xff1a;利用摄像头等设备获取用户…

2023鲁大师评测沟通会:鲁大师尊享版登场、“鲁小车”正式上线

作为硬件评测界的“老兵”&#xff0c;鲁大师不仅有着十几年的硬件评测经验&#xff0c;并且一直都在不断地尝试、不断地推陈出新。在5月9日举行的“2023年鲁大师评测沟通会”上&#xff0c;鲁大师向大众展示了在过去一年间取得的成果。 PC业务迭代升级&#xff0c;鲁大师客户端…

day(2,3)-内核模块

内核模块上 主要内容 向内核添加新功能 内核模块基础代码讲解 内核模块多源文件编程 内核模块信息宏 一、向内核添加新功能 1.1 静态加载法&#xff1a; 即新功能源码与内核其它代码一起编译进uImage文件内 Kconfig是make menuconfig的界面配置文件 1.2动态加载法&am…

Yolov8改进---注意力机制:DoubleAttention、SKAttention,SENet进阶版本

目录 🏆🏆🏆🏆🏆🏆Yolov8魔术师🏆🏆🏆🏆🏆🏆 1. DoubleAttention 2. SKAttention 3.总结

2022-4-4基于单片机的MQ2烟雾传感器报警系统设计

基于单片机的MQ2烟雾传感器报警系统设计 源代码和仿真图及MQ2相关资料可在文末的链接中下载 该系统实现的功能&#xff1a;检测空气中的烟雾浓度并实现超阈值报警 该系统组成由&#xff1a; 单片机最小系统、ADC0832、MQ2烟雾传感器、LCD1602液晶模块、声光报警模块、按键模…

研究人员发现微软Azure API管理服务存在3个漏洞

微软Azure API管理服务中披露了三个新的安全漏洞&#xff0c;恶意行为者可能会滥用这些漏洞来访问敏感信息或后端服务。 据以色列云安全公司Ermetic称&#xff0c;这包括两个服务器端请求伪造(SSRF)漏洞和API管理开发人员门户中的一个不受限制的文件上传功能实例。 安全研究员…

【腾讯云 Finops Crane 集训营】Crane应用实战

文章目录 前言一、Crane是什么&#xff1f;二、Crane的特点三、Crane使用1、环境准备2、安装Crane3、访问dashboard4、页面展示5、功能应用 四、Crane的优势总结 前言 FinOps&#xff08;Financial Operations&#xff09;是一种管理云计算成本的方法&#xff0c;它强调将云计…

使用PyTorch构建神经网络,并使用thop计算参数和FLOPs

文章目录 使用PyTorch构建神经网络&#xff0c;并使用thop计算参数和FLOPsFLOPs和FLOPS区别使用PyTorch搭建神经网络整体代码1. 导入必要的库2. 定义神经网络模型3. 打印网络结构4. 计算网络FLOPs和参数数量5. 结果如下手动计算params手动计算FLOPs注意 使用PyTorch构建神经网络…

车联网强势发展下,有什么隐患?

通过新一代信息通信技术&#xff0c;车联网实现了汽车与云平台&#xff0c;车辆和汽车&#xff0c;道路&#xff0c;汽车和人以及内部的全方位网络链接。车联网使用传感器技术感知车辆的状态信息&#xff0c;并利用无线通信网络和现代智能信息处理技术的帮助实现交通智能化管理…

FPGA入门系列10--按键消抖

文章简介 本系列文章主要针对FPGA初学者编写&#xff0c;包括FPGA的模块书写、基础语法、状态机、RAM、UART、SPI、VGA、以及功能验证等。将每一个知识点作为一个章节进行讲解&#xff0c;旨在更快速的提升初学者在FPGA开发方面的能力&#xff0c;每一个章节中都有针对性的代码…

Redis高可用系列——Set类型底层详解

文章目录 概述intsetintset 和 hashtable 的转换为什么加入了listpackhashtable 的空间开销高hashtable 的碰撞概率高intset 、listpack和hashtable的转换 概述 在讲解set结构之前&#xff0c;需要先说明一下set结构编码的更替&#xff0c;如下 在Redis7.2之前&#xff0c;se…

Ansys Lumerical | CMOS - 光学仿真方法

通过使用更小的像素尺寸和更大的填充因子&#xff0c;基于CMOS图像传感器像素的数码相机系统的成本正在降低。但是&#xff0c;只有在不牺牲图像质量的情况下&#xff0c;CMOS像素尺寸减小才是可以接受的。随着CMOS像素尺寸的不断减小&#xff0c;图像信噪比降低&#xff0c;相…

《我命由我不由天》蔡志忠——笔记一

目录 简介 经典摘录 三岁决定一生 父母该什么时候放手 确定将来要成为什么 积极主动为目标而努力 叛逆是最伟大的创意 父亲给蔡志忠最大的影响是教会他两件事 价值观缺陷导致的后果 人有三个阶段 简介 作者 蔡志忠&#xff0c;李虹。 蔡志忠&#xff1a;漫画家、哲…

力扣-1769. 移动所有球到每个盒子所需的最小操作数

题目&#xff1a; 有 n 个盒子。给你一个长度为 n 的二进制字符串 boxes &#xff0c;其中 boxes[i] 的值为 ‘0’ 表示第 i 个盒子是 空 的&#xff0c;而 boxes[i] 的值为 ‘1’ 表示盒子里有 一个 小球。 在一步操作中&#xff0c;你可以将 一个 小球从某个盒子移动到一个与…

搜索引擎优化SEO和SEM有什么不一样

SEO&#xff08;搜索引擎优化&#xff09;和SEM&#xff08;搜索引擎营销&#xff09;都是用于提高网站在搜索引擎中的排名和能见度的技术。虽然它们的目标是相同的&#xff0c;但它们的方法和重点略有不同&#xff0c;今天和大家聊聊SEO和SEM有什么不同。 一、SEO SEO是指通…

红帽8配置yum源

使用传输工具 上传文件到/etc/yum.repos.d/ 或 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo 注&#xff1a;不能下载wget直接上传文件 关闭订阅插件提示 [redhatroot ~]$ vi /etc/yum.conf #添加 plugins0 [redhatroot ~]$ …

docker+jenkins自动化部署springboot项目

前置:环境配置 阿里云服务器1核2GBjava 1.8.0._371maven apache-maven3.8.8git 1.8.3.1 docker和jenkins在一台服务器上&#xff0c;环境的配置&#xff0c;对于java和maven可以自己下载对应tar包进行配置&#xff0c;记得配置环境&#xff0c;也可以使用yum进行安装。记得配置…

FS5175AE降压型1-4节锂电池充电芯片

FS5175AE是一款工作于5V到24V的多串锂电池同步开关降压充电管理芯片。内置MOS管集成了低导通阻抗的NMOS&#xff0c;FS5175AE采用1MHz同步开关架构&#xff0c;实现高 效率充电并简化外围器件&#xff0c;降低BOM成本。通过调节检测电阻&#xff0c;可实现**2A充电电流&#xf…