【数据结构】快速排序(详解)

目录

快速排序

历史:

基本思想:

主框架:

下面解释实现单次排序的几种版本:

1.Hoare版本

2. 挖坑法

3. 前后指针法

快速排序的实现包括递归与非递归:

1. 递归实现:(即开头的基本框架)

2. 非递归:(迭代)

下面我们通过栈来演示非递归实现快速排序:


快速排序

历史:

快速排序是Hoare(霍尔)于1962年提出的一种二叉树结构的交换排序方法

基本思想:

任取待排序元素序列中的一个元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后在左右子序列重复该过程,直到所有元素都排序在相应位置上为止

主框架:

上述为快速排序递归实现的主框架,我们可以发现它和二叉树的前序遍历十分相似 

下面解释实现单次排序的几种版本:
1.Hoare版本

单趟动图演示:

步骤:

  1. 选择基准值:通常选择序列的第一个元素或最后一个元素作为基准值。

  2. 设置两个int变量记录数组下标:一个记录序列的开始的下标(left),另一个记录序列的末尾的下标(right)。

  3. 开始分区

    • 先让right++,直到找到一个比基准值大的元素。
    • 再让left--,直到找到一个比基准值小的元素。
    • 交换这两个元素的位置,并继续right++。
    • 重复上述步骤,直到left >= right。
    • 注意:right先加,保证下一步骤与基准值交换的值小于基准值
  4. 交换基准值与right位置的值:此时,基准值左边的所有元素都比基准值小,基准值右边的所有元素都比基准值大。

代码实现:

int PartSort1(int* a, int left, int right) {int key = left;while (left < right) {while(a[right] >= a[key]&&left < right) {right--;}while(a[left] <= a[key]&&left < right) {left++;}swap(&a[left], &a[right]);}swap(&a[key], &a[right]);return left;
}
2. 挖坑法

单趟动图演示:

步骤:

  1. 选择基准元素:在待排序的序列中,选择一个元素作为基准元素(key)。这个元素可以是序列的第一个元素、最后一个元素或者任意一个元素,甚至是随机选取的一个元素。

  2. 挖坑:将基准元素从原位置移除,形成一个“坑”(hole)。

  3. 填坑:从序列的一端开始(可以是左端或右端),将遇到的第一个比基准元素小(或大)的元素填入坑中,并形成一个新的坑。这里有两种情况:

    • 如果从右向左遍历,遇到比基准元素小的元素,则将其填入坑中,并继续从左向右遍历。这里代码实现选择该情况
    • 如果从左向右遍历,遇到比基准元素大的元素,则将其填入坑中,并继续从右向左遍历。
  4. 此时,基准元素左边的所有元素都小于基准元素,右边的所有元素都大于或等于基准元素。

代码实现:

int PartSort2(int* a, int left, int right) {int x = a[left];int key = left;while (left < right) {while (a[right] >= x && left < right) {right--;}a[key] = a[right];key = right;while (a[left] <= x && left < right) {left++;}a[key] = a[left];key = left;}a[key] = x;return key;
}
3. 前后指针法

单趟动图演示:

基本步骤:

  • 从待排序的数组中选择一个元素(key)作为基准。通常选择第一个或最后一个元素作为基准,但也可以随机选择。
  • 设置两个下标,一个记录序列的开始的下标(pre),即pre=left,另一个记录pre下一个元素的下标的下标(cur),即cur=pre+1;
  • 当(cur<=right)时,循环执行:cur找比key小的元素,如果a[cur]<=a[key],交换a[cur]和a[pre],否则pre++,每次循环cur++

代码实现:

int PartSort3(int* a, int left, int right) {int key = left;int pre = left;int cur = pre + 1;while (cur <= right) {if (a[cur] <= a[key] && ++pre != cur) {swap(&a[cur], &a[pre]);}cur++;}swap(&a[key], &a[pre]);return pre;
}
快速排序的实现包括递归与非递归:
1. 递归实现:(即开头的基本框架)
void QuickSort(int* a, int left, int right) {if (left >= right) {return;}//int key = PartSort1(a, left, right);//int key = PartSort2(a, left, right);int key = PartSort3(a, left, right);QuickSort(a, left, key - 1);QuickSort(a, key + 1, right);
}
2. 非递归:(迭代)

在非递归版本中,我们使用栈(或队列)来保存待排序的子数组的起始和结束索引,而不是直接递归调用;栈相当于前序遍历,而队列相当于层序遍历

下面我们通过栈来演示非递归实现快速排序:

思路与步骤:

  1. 循环处理栈:当栈不为空时,执行以下步骤:

  • 弹出栈顶的两个元素,它们分别表示当前子数组的起始和结束索引。
  • 如果起始索引等于结束索引(即子数组只有一个元素),那么直接跳过,不需要排序。
  • 调用PartSort3函数对当前子数组进行划分,并返回划分后的基准索引key
  • 弹出之前压入的起始和结束索引(因为已经处理过这个子数组了)。
  • 根据key的位置,决定下一步的操作:
    • 如果key等于起始索引left,说明基准左边的元素都已经排好序,只需要对基准右边的元素进行排序,所以将key + 1right压入栈中。
    • 如果key等于结束索引right,说明基准右边的元素都已经排好序,只需要对基准左边的元素进行排序,所以将leftkey - 1压入栈中。
    • 如果key既不等于left也不等于right,说明基准左右两边都有需要排序的元素,所以将leftkey-1key+1right的子数组分别压入栈中
  • 2.完成:当栈为空时,表示所有子数组都已排序完成,此时整个数组也已排序完成。

代码实现:

typedef int StackNode;
typedef struct Stack {StackNode* a;int real;int capacity;
}Stack;
void InitStack(Stack* p) {assert(p);p->a = NULL;p->capacity = p->real = 0;
}
//扩容
void expansion(Stack*p) {int newcapacity = p->capacity == 0 ? 4 : 2 * p->capacity;StackNode* tmp = (StackNode*)realloc(p->a, sizeof(StackNode) * newcapacity);if (tmp == NULL) {perror("expansion::realloc::NULL");exit(0);}p->a = tmp;p->capacity = newcapacity;
}
//入栈
void Push(Stack*p,int x) {assert(p);if (p->capacity == p->real) {expansion(p);}p->a[p->real++] = x;
}
//出栈
void Pop(Stack* p) {assert(p);p->real--;
}
void StackDestroy(Stack* p) {assert(p);free(p->a);p->a = NULL;p->capacity = p->real = 0;
}
//快排代码实现
void QuickSortNonR(int* a, int left, int right) {if (left >= right) {return;}Stack stack;InitStack(&stack);Push(&stack, left);Push(&stack, right);while (stack.real) {left = stack.a[stack.real - 2];right = stack.a[stack.real - 1];if (left == right) {Pop(&stack);Pop(&stack);continue;}int key = PartSort3(a, left, right);Pop(&stack);Pop(&stack);if (key == left) {Push(&stack, key + 1);Push(&stack, right);continue;}if (key == right) {Push(&stack, left);Push(&stack, key - 1);continue;}Push(&stack, key + 1);Push(&stack, right);Push(&stack, left);Push(&stack, key - 1);}StackDestroy(&stack);
}

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

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

相关文章

Jenkins安装 :AWS EC2 Linux

1 JDK11 install # 用的yum安装 # 压缩包安装&#xff0c;下载的jdk-11.0.22_linux-x64_bin.tar.gz在EC2解压&#xff0c;配置环境变量&#xff0c;运行jenkins的时候会报错$ yum -y list java-11* Available Packages java-11-amazon-corretto-devel.x86_64 …

Linux shell 实现自增版本号

Linux shell脚本实现x.x.x格式递增版本号&#xff0c;每次运行脚本自动将最后一位版本号1&#xff0c;每次更新后将最新的版本号保存到当前txt文件内容&#xff0c;以保持连续递增版本号。 脚本文件目录 rootubuntu01:/scripts/version# tree . ├── increment_version.sh └…

动手学深度学习4.6 暂退法-笔记练习(PyTorch)

以下内容为结合李沐老师的课程和教材补充的学习笔记&#xff0c;以及对课后练习的一些思考&#xff0c;自留回顾&#xff0c;也供同学之人交流参考。 本节课程地址&#xff1a;丢弃法_哔哩哔哩_bilibili 本节教材地址&#xff1a;4.6. 暂退法&#xff08;Dropout&#xff09;…

LangChain技术解密:构建大模型应用的全景指南

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

Flutter 中的 UnconstrainedBox 小部件:全面指南

Flutter 中的 UnconstrainedBox 小部件&#xff1a;全面指南 Flutter 提供了众多的布局小部件&#xff0c;以帮助开发者构建灵活且响应式的用户界面。UnconstrainedBox 是其中一种特殊的小部件&#xff0c;它允许子组件在没有约束的情况下渲染&#xff0c;同时可以指定一个父组…

深入理解Java中的this关键字

在Java编程中&#xff0c;this关键字是一个非常重要且常用的概念。对于初学者来说&#xff0c;理解this关键字的实际用途和工作原理&#xff0c;对于掌握面向对象编程&#xff08;OOP&#xff09;的基本概念至关重要。本篇博客将详细讲解this关键字的各种用法及其背后的机制&am…

2020职称继续教育--突发公共卫生事件应急处理技术方案

单选题&#xff08;共7题&#xff0c;每题5分&#xff09; 1、滑塌、滑坡、泥石流属于&#xff08;&#xff09;。 C、地质灾害 2、本讲中&#xff0c;不属于卫生应急管理体制原则的是&#xff08;&#xff09;。 B、综合协调 3、自然灾害卫生工作周期不包括&#xff08;&…

使用目标检测模型YOLO V9 OBB进行旋转目标的检测:训练自己的数据集(基于卫星和无人机的农业大棚数据集)

我看到YOLO V8中&#xff08;ultralytics版本8.2.18&#xff09;集成了YOLO V9&#xff0c;所以直接在YOLO V8 OBB的基础上实现YOLO V9 OBB&#xff0c;训练结果也出来了&#xff0c;但是评估指标比YOLO V8 OBB低一点点&#xff0c;不知道是不是哪里遗漏修改了......如有大神赐…

公用表表达式(CTE)详解:针对 MySQL 和 SQL Server 数据库

公用表表达式&#xff08;CTE&#xff0c;Common Table Expressions&#xff09;是一种在 SQL 中定义临时结果集的方法&#xff0c;该结果集在单个查询的执行过程中可以被引用。CTE 提高了查询的可读性和结构化&#xff0c;特别适用于复杂的子查询和递归查询。本文将详细介绍 C…

cfa三级大神复习经验分享系列(六)

Behavioral Finance 这一般是大家拿来入门的章节&#xff0c;可是上来就那么多名词&#xff0c;那么多原理真的完全看晕了。其实这一章节最大的重点就是各个bias&#xff0c;其他的东西都是帮助理解的&#xff0c;考试不会考。我自己列了一个清单&#xff0c;把所有bias根据分类…

【Linux学习】进程间通信 (2) —— 信号

下面是有关进程通信中信号的相关介绍&#xff0c;希望对你有所帮助&#xff01; 小海编程心语录-CSDN博客 目录 1. 信号 1.1 概念 1.2 信号的产生 1.3 信号的处理方式 2. 函数 2.1 kill() 函数 2.2 signal()函数 2.3 sigaction()函数 2.4 sigprocmask()函数 …

Python中的`*args`和`**kwargs`:深入理解可变参数传递

Python中的*args和**kwargs:深入理解可变参数传递 在Python函数编程中,*args和**kwargs是两个非常有用的特性,它们允许我们在调用函数时传递任意数量和类型的参数。这种灵活性使得函数更加通用和可重用。本文将深入探讨*args和**kwargs的工作原理、应用场景以及它们如何增强…

c++对rgb数据进行抽样

以下算法是对rgb数据进行抽样,将数据在内容不变的情况下,降低数据的内存占用,方便应用处理和网络传输。 int scaleRGB(const unsigned char* src_img, unsigned char* dst_img, unsigned int src_width, unsigned int src_height, unsigned int dst_width, unsigned int ds…

文盘Rust -- 生命周期问题引发的 static hashmap 锁

100编程书屋_孔夫子旧书网 2021年上半年,撸了个rust cli开发的框架,基本上把交互模式,子命令提示这些cli该有的常用功能做进去了。项目地址:https://github.com/jiashiwen/interactcli-rs。 春节以前看到axum已经0.4.x了,于是想看看能不能用rust做个服务端的框架。 春节…

如何从Android恢复已删除的文件?3 种有效的方式

有时我们可能会错误地删除Android设备上的重要文件。更疯狂的是&#xff0c;Android手机上的文件在一夜之间消失了&#xff0c;我们不知道为什么。我们感到非常遗憾和恼火&#xff0c;但不知道。但是&#xff0c;此时学习如何从Android手机恢复已删除的文件为时已晚&#xff0c…

Excel 取出每组最后一行

Excel的前两列是两层的分组列&#xff0c;后两列是明细 ABCD1CM11112CM12123CM13134CM14145CM25156CM26167BM11218BM12229BM232310AM113111AM323212AM333313AM3434 现在要取出每小组的最后一行&#xff1a; ABCD1CM14142CM26163BM12224BM23235AM11316AM3434 使用 SPL XLL sp…

拼多多商品详情商品标题sku等信息抓取接口API调用步骤演示

接口名称&#xff1a;item_get_app_pro 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_search_sho…

两台电脑怎么互传文件?这些方法你值得一试

在日常生活和工作中&#xff0c;我们经常需要在不同电脑之间传输文件&#xff0c;这可能是文档、照片、音乐或其他类型的文件。两台电脑怎么互传文件是非常有用的技能&#xff0c;可以提高工作效率并简化文件共享过程。本文将介绍三种常见的方法&#xff0c;帮助您了解如何在两…

先进制造aps专题十 aps项目成功指南

aps项目成功指南 为了保证aps项目的成功 现在国内的aps项目 一是看aps软件本身是不是实现了复杂的排程算法和优化算法&#xff0c;算法引擎使用c高性能编译语言开发&#xff0c;支持工序的复杂关系&#xff0c;考虑副资源约束和特殊规格约束&#xff0c;提供了能考虑各种约束…

2020职称继续教育--石化企业突发事件 应急管理及典型案例分析

单选题&#xff08;共7题&#xff0c;每题5分&#xff09; 1、生产经营单位应当在编制应急预案的基础上&#xff0c;针对工作场所、岗位的特点&#xff0c;编制简明、实用、有效的&#xff08;&#xff09;。 A、应急处置卡 2、&#xff08;&#xff09;快速响应的是世界一流企…