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

目录

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无语

以上

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

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

相关文章

TypeScript 最近各版本主要特性总结

&#xff08;在人生的道路上&#xff0c;当你的期望一个个落空的时候&#xff0c;你也要坚定&#xff0c;要沉着。——朗费罗&#xff09; TypeScript 官网 在线运行TypeScript代码 第三方中文博客 特性 typescript是javascript的超集&#xff0c;向javascript继承额外的编辑…

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

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

干货满满!破解FP安全收款难题

怎样安全收款是做擦边产品卖家比较忧虑的问题&#xff0c;2023年已经即将来到了年中&#xff0c;跨境卖家们在这一方面做得怎么样了呢&#xff1f; 这期分享破解FP独立站收款难题的方法。 一、商家破解FP收款难题方法 1.第三方信用通道 优点&#xff1a;信用卡在国外使用率比…

好家伙,又一份牛逼笔记面世了...

最近网传的一些裁员的消息&#xff0c;搞的人心惶惶。已经拿到大厂offer的码友来问我&#xff1a;大厂还能去&#xff0c;去了会不会被裁。 还在学习的网友来问我&#xff1a;现在还要冲互联网么&#xff1f; 我是认为大家不用恐慌吧&#xff0c;该看啥看啥&#xff0c;该学啥…

ASEMI代理ADI亚德诺ADUM3211TRZ-RL7原厂芯片

编辑-Z ADUM3211TRZ-RL7参数描述&#xff1a; 型号&#xff1a;ADUM3211TRZ-RL7 数据速率&#xff1a;10 Mbps 传播延迟&#xff1a;50 ns 脉冲宽度失真&#xff1a;3 ns 脉冲宽度&#xff1a;100 ns 输出上升/下降时间&#xff1a;2.5 ns 供电电流&#xff1a;2.6 mA …

Maven与spring学习

目录 该如何学习Maven&#xff0c;是先该学习spring还是先学习Maven 能讲一下该如何学习Maven吗&#xff1f; 火狐浏览器有能让网页翻译成为中文的插件吗 秋田和柴犬是同一个狗吗 该如何学习Maven&#xff0c;是先该学习spring还是先学习Maven 学习Maven可以与学习Spring同…

一键安装k8s脚本

服务器配置 节点(华为云服务器)配置master 2vCPUs | 4GiB | s6.large.2 CentOS 7.8 64bit node1 2vCPUs | 8GiB | s6.large.4 CentOS 7.8 64bit node2 2vCPUs | 8GiB | s6.large.4 CentOS 7.8 64bit 1.master节点安装脚本&#xff1a;install_k8s_master.sh。 sh文件上传到…

2023数维杯数学建模ABC题思路分析

占个位置吧&#xff0c;开始在本帖实时更新数维杯数学建模赛题思路代码&#xff0c;文章末尾获取&#xff01; 持续为更新参考思路 赛题思路 已完成全部可以领取&#xff0c;详情看文末&#xff01;&#xff01;&#xff01; 数维杯A题思路 A题是这次比赛比较难的题目&…

windows下升级nodejs

重新安装新版nodejs 重新安装nodejs然后设置环境变量 安装yarn npm install -g yarn --registryhttps://registry.npm.taobao.org yarn config set registry https://registry.npm.taobao.org -g yarn config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sa…

大数据如何助力营销(2)用户画像

用户画像是指根据用户的数据&#xff0c;构建出用户的特征和兴趣&#xff0c;从而对用户进行分类和个性化的过程。用户画像可以帮助营销人员更有效地触达目标客户&#xff0c;提高营销效果和转化率。 用户画像的价值 用户画像的价值主要体现在以下几个方面&#xff1a; 提升用…

信号signal编程测试

信号会打断系统调用&#xff0c;慎用&#xff0c;就是用的时候测一测。 下面是信号的基础测试 信号 信号&#xff08;signal&#xff09;机制是UNIX系统中最为古老的进程之间的通信机制。它用于在一个或多个进程之间传递异步信号。信号可以由各种异步事件产生&#xff0c;例如…

计算机毕业论文内容参考|基于三维建模和卷积神经网络的人脸转正的技术设计

文章目录 导文文章重点摘要前言绪论课题背景国内外现状与趋势课题内容相关技术与方法介绍技术分析技术设计人脸转正方法卷积神经网络的训练和优化数据预处理技术实现总结与展望本文总结导文 基于java开发汽车销售系统资料 文章重点 摘要 在实际应用中,人脸图像往往具有旋转、…

vue+elementui+nodejs校园高校餐厅点餐及订餐菜品推荐评价系统6927k

传统的销售模式&#xff0c;在实体店的紧跟式的销售模式&#xff0c;会给消费者一种不自由&#xff0c;被监视的感觉。餐厅点餐及推荐系统&#xff0c;紧跟数据时代的步伐&#xff0c;使用nodejs开发语言&#xff0c;配备MySQL数据库。扎根于实际问题所开发出来的一套系统。这个…

【C进阶】-- 字符串函数(1)

目录 0. 前言 1. 函数介绍 1.1 strlen 1.1.1主动改变\0的位置 ✅"strlen函数的返回类型是size_t - 无符号整型"✅ 当使用strlen函数但不引用头文件时&#xff0c;执行结果超出预料: 求字符串长度的方法&#x1f4a5; 1.计数器 2.递归 3.指针 - 指针 1.2 st…

项目集战略一致性

项目集战略一致性是识别项目集输出和成果&#xff0c;以便与组织的目标和目的保持一致的绩效领域。 本章内容包括&#xff1a; 1 项目集商业论证 2 项目集章程 3 项目集路线图 4 环境评估 5 项目集风险管理战略 项目集应与组织战略保持一致&#xff0c;并促进组织效益的实现。为…

粒子群算法(PSO)

理论&#xff1a; 粒子群优化算法&#xff08;PSO&#xff09;是一种智能优化算法&#xff0c;也是一种元启发式算法&#xff0c;最初是由Eberhart和Kennedy提出的&#xff0c;其模拟了鸟群捕食行为&#xff0c;通过一定的搜索策略&#xff0c;使得多个粒子在多维搜索空间中寻…

Java+Python+Paddle提取长文本文章中词频,用于Echart词云图数据

公司有个需求&#xff0c;就是需要提供给echart词云图的数据&#xff0c;放在以前我们的数据来源都是从产品那直接要&#xff0c;产品也是跑的别的接口&#xff0c;那怎么行呢&#xff0c;当然有自己的一套可以随便搞了&#xff0c;那么操作来了 Java package cn.iocoder.yud…

第十四届蓝桥杯大赛软件赛省赛(Java 大学A组)

蓝桥杯 2023年省赛真题 Java 大学A组 试题 A: 特殊日期  试题 B: 与或异或  试题 I: 高塔 把填空挂上跟大伙对对答案&#xff0c;然后 I \rm I I 题出的还行就先讲讲&#xff0c;剩下的最近有点忙&#xff0c;先放放。 试题 A: 特殊日期 本题总分&#xff1a;5 分 【问题描…

PMP课堂模拟题目及解析(第5期)

41. 项目的混凝土供应商通知项目经理&#xff0c;材料将比预定时间晚三个星期交付。项目经理更新了进度计划并通知项目团队。在这种情况下&#xff0c;哪种合同类型承担的风 险最小&#xff1f; A. 总价加激励费用合同。 B. 总价加经济价格调整合同。 C. 工料合同。 D. 固…

利用阿里云免费部署openai的Chatgpt国内直接用

背景 国内无法直接访问ChatGPT&#xff0c;一访问就显示 code 1020。而且最近OpenAI查的比较严格&#xff0c;开始大规模对亚洲地区开始封号&#xff0c;对于经常乱跳IP的、同一个ip一堆账号的、之前淘宝机刷账号的&#xff0c;账号被封的可能性极大。 那么有没有符合openai规定…