为何快速排序算法在左右都等于pivot基准值时还要进行一次交换?

本文主要是介绍为何快速排序算法在左右都等于pivot基准值时还要进行一次交换?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:

快排作为世界十大经典算法之一,其重要性就不再多谈了。 我在学习快排过程中有一个很大的疑惑是为什么在进行从左到右和从右到左的扫描时,如果左右都等于pivot基准值还要进行一次交换。这其实是因为一些其他场景的处理导致的一些没法避免的小问题。下面我们详细分析他真正想处理的问题是什么。

注意:快排有非常多的写法,我们只能针对一段代码中的扫描和什么时候停止扫描进行元素交换的逻辑进行分析。不能说本文的解析适用于所有人写的快排。

话不多说,先上java代码:
public class Solution {public void sortArray(int[] A) {if (A == null) return;helper(A, 0, A.length - 1);}private void helper(int[] A, int left, int right) {if (left >= right) {return;}int pivot = A[left + (right - left) / 2];int i = left, j = right;while (i <= j) {while (i <= j && A[i] < pivot) {i++;}while (i <= j && A[j] > pivot) {j--;}if (i <= j) {int tmp = A[i];A[i] = A[j];A[j] = tmp;i++;j--;}}helper(A, left, j);helper(A, i, right);}
}

分析为何左右都等于pivot时还要做一次交换

这里从代码来看也就是说为什么while (i <= j && A[i] < pivot)不能写成while (i <= j && A[i] <= pivot)

  1. 这里我们使用举例法, 比如一个A数组是[1,1,1]如果A[i] <= pivot都要继续扫描不停止,那么i会一直增长到3,大于j导致后面的while循环条件不满足。 后面去递归处理子问题时会成为helper(A, 0, 2)的情况,因为j都没有机会移动就被i跑到j右边去了,这就导致了无限循环永远结束不了的bug。
  2. 我们再举个例子, 比如数组A是[2,3,1],我们还假设A[i] <= pivot时不停止 。又出现了上面的情况,无限循环
  3. 但并不是所有的数组情况都会出错,比如如果换一个数组A[3,2,1,4,5]并还假设A[i] <= pivot时不停止那么是可以得到正确结果的。我们可以详细来画一下。
假设while (i <= j && A[i] <= pivot)时扫描继续,不停止,i继续执行++操作
[3,2,1,4,5]i   p   ji   j
[1,2,3,4,5]ijj 
此时因为 i >= j不再满足所以不进行交换和while循环, 第一轮结束
然后会有递归的两个式子:
helper(A, 0, 0);  //left == right 直接就return了
helper(A, 1, 4);
[1,2,3,4,5]i p   jij
此时因为 i >= j不再满足所以不进行交换和while循环, 第二轮结束
然后会有递归的两个式子:
helper(A, 1, 2);  //left == right 直接就return了
helper(A, 3, 4);
(1) 先看 helper(A, 1, 2);
[1,2,3,4,5]i jpij
此时因为 i >= j不再满足所以不进行交换和while循环, 第三轮第一组结束
然后会有递归的两个式子:
helper(A, 1, 1);  //left == right 直接就return了
helper(A, 2, 2); //left == right 直接就return了
(2) 再看 helper(A, 3, 4);
[1,2,3,4,5]i jpj i
此时因为 i >= j不再满足所以不进行交换和while循环, 第三轮第二组结束	    
然后会有递归的两个式子:
helper(A, 3, 3);  //left == right 直接就return了
helper(A, 4, 4); //left == right 直接就return了这样看来等于pivot时不交换对于这个序列没有影响
  1. 再举一个会出错的例子[5,4,3,2]。我们也可以详细来画一下。
[5,4,3,2]i p   j
[2,4,3,5]i jpi
此时因为 i >= j不再满足所以不进行交换和while循环, 第一轮结束
然后会有递归的两个式子:
helper(A, 0, 2);  
helper(A, 3, 3); //left == right 直接就return了
然后我们只用看helper(A, 0, 2); 
[2,4,3,5]i   ji(//越界)
此时j都没机会动i就大于j了, while循环结束。继续进入递归的式子
helper(A, 0, 2);  
helper(A, 3, 2); //left >= right 直接就return了
然后helper(A, 0, 2);  就无限循环导致stack overflow栈内存溢出了

这篇关于为何快速排序算法在左右都等于pivot基准值时还要进行一次交换?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/949943

相关文章

Nginx中配置使用非默认80端口进行服务的完整指南

《Nginx中配置使用非默认80端口进行服务的完整指南》在实际生产环境中,我们经常需要将Nginx配置在其他端口上运行,本文将详细介绍如何在Nginx中配置使用非默认端口进行服务,希望对大家有所帮助... 目录一、为什么需要使用非默认端口二、配置Nginx使用非默认端口的基本方法2.1 修改listen指令

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

MySQL按时间维度对亿级数据表进行平滑分表

《MySQL按时间维度对亿级数据表进行平滑分表》本文将以一个真实的4亿数据表分表案例为基础,详细介绍如何在不影响线上业务的情况下,完成按时间维度分表的完整过程,感兴趣的小伙伴可以了解一下... 目录引言一、为什么我们需要分表1.1 单表数据量过大的问题1.2 分表方案选型二、分表前的准备工作2.1 数据评估

C#使用Spire.XLS快速生成多表格Excel文件

《C#使用Spire.XLS快速生成多表格Excel文件》在日常开发中,我们经常需要将业务数据导出为结构清晰的Excel文件,本文将手把手教你使用Spire.XLS这个强大的.NET组件,只需几行C#... 目录一、Spire.XLS核心优势清单1.1 性能碾压:从3秒到0.5秒的质变1.2 批量操作的优雅

MySQL进行分片合并的实现步骤

《MySQL进行分片合并的实现步骤》分片合并是指在分布式数据库系统中,将不同分片上的查询结果进行整合,以获得完整的查询结果,下面就来具体介绍一下,感兴趣的可以了解一下... 目录环境准备项目依赖数据源配置分片上下文分片查询和合并代码实现1. 查询单条记录2. 跨分片查询和合并测试结论分片合并(Shardin

Mybatis-Plus 3.5.12 分页拦截器消失的问题及快速解决方法

《Mybatis-Plus3.5.12分页拦截器消失的问题及快速解决方法》作为Java开发者,我们都爱用Mybatis-Plus简化CRUD操作,尤其是它的分页功能,几行代码就能搞定复杂的分页查询... 目录一、问题场景:分页拦截器突然 “失踪”二、问题根源:依赖拆分惹的祸三、解决办法:添加扩展依赖四、分页

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c

C++归并排序代码实现示例代码

《C++归并排序代码实现示例代码》归并排序将待排序数组分成两个子数组,分别对这两个子数组进行排序,然后将排序好的子数组合并,得到排序后的数组,:本文主要介绍C++归并排序代码实现的相关资料,需要的... 目录1 算法核心思想2 代码实现3 算法时间复杂度1 算法核心思想归并排序是一种高效的排序方式,需要用

SpringBoot结合Knife4j进行API分组授权管理配置详解

《SpringBoot结合Knife4j进行API分组授权管理配置详解》在现代的微服务架构中,API文档和授权管理是不可或缺的一部分,本文将介绍如何在SpringBoot应用中集成Knife4j,并进... 目录环境准备配置 Swagger配置 Swagger OpenAPI自定义 Swagger UI 底

基于Python Playwright进行前端性能测试的脚本实现

《基于PythonPlaywright进行前端性能测试的脚本实现》在当今Web应用开发中,性能优化是提升用户体验的关键因素之一,本文将介绍如何使用Playwright构建一个自动化性能测试工具,希望... 目录引言工具概述整体架构核心实现解析1. 浏览器初始化2. 性能数据收集3. 资源分析4. 关键性能指