快速排序中的分割算法的解析与应用

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

一,分割(partition)算法介绍

所谓分割算法,先选定一个枢轴元素,然后 将数组中的元素分成两部分:比枢轴元素小的部分都位于枢轴元素左边;比枢轴元素大的部分都位于枢轴元素右边

此时,枢轴元素在数组中的位置就被“永久地确定”下来了---将整个数组排序,该枢轴元素的位置不会变化。

另外,枢轴元素的选取对分割算法至关重要。一般而言,终极追求的是:将数组平分。因此,尽可能地让枢轴元素的选取随机化和靠近中位数。

这里采用“三数取中”法选取枢轴元素。

关于快速排序排序算法,可参考:http://www.cnblogs.com/hapjin/p/5518922.html

 

二,分割算法的实现

复制代码
 1 //分割数组,将数组分成两部分. 一部分比pivot(枢轴元素)大,另一部分比pivot小
 2     private static int parition(int[] arr, int left, int right){
 3         
 4         int pivot = media3(arr, left, right);
 5         int i = left;
 6         int j = right - 1;//注意 ,在 media3()中 arr[right-1]就是 pivot
 7         
 8         for(;;)
 9         {
10             while(arr[++i] < pivot){}
11             while(arr[--j] > pivot){}
12             if(i < j)
13                 swap(arr, i, j);
14             else
15                 break;
16         }
17         
18         swap(arr, i, right-1);//restore pivot, 将枢轴元素放置到合适位置:arr左边元素都比pivot小,右边都比pivot大
19         return i;// 返回 pivot的 索引
20     }
复制代码

①第4行,枢轴元素是通过“三数取中”法选择的。在“三数取中”时,还做了一些优化:将 枢轴元素 放到 数组末尾的倒数第二个位置处。具体参考 media3()
需要注意的是:当输入的数组中长度为1 或者 2 时, partition会出现向下越界(但对快排而言,当数组长度很小的,其实可以不用 partition,而是直接用插入排序)。因此,可加入以下的修改。

复制代码
 1 //分割数组,将数组分成两部分. 一部分比pivot(枢轴元素)大,另一部分比pivot小
 2     private static int parition(int[] arr, int left, int right){
 3         
 4         int pivot = media3(arr, left, right);
 5         int i = left;
 6         int j = right - 1;//注意 ,在 media3()中 arr[right-1]就是 pivot
 7         
 8         //应对特殊情况下的数组,比如数组长度 小于3
 9         if(i >= j)
10             return i;
11         
12         for(;;)
13         {
14             while(arr[++i] < pivot){}
15             while(arr[--j] > pivot){}
16             if(i < j)
17                 swap(arr, i, j);
18             else
19                 break;
20         }
21         
22         swap(arr, i, right-1);//restore pivot 将枢轴元素放置到合适位置:arr左边元素都比pivot小,右边都比pivot大
23         return i;// 返回 pivot的 索引
24     }
复制代码

 

再来看看,三数取中算法,这里也有个特殊情况:当数组中元素个数都没有3个时....怎么办?

复制代码
 1     //三数取中,用在快排中随机选择枢轴元素时
 2     private static int media3(int[] arr, int left, int right){
 3         if(arr.length == 1)
 4             return arr[0];
 5         
 6         if(left == right)
 7             return arr[left];
 8         
 9         int center = (left + right) / 2;
10         
11         //找出三个数中的最小值放到 arr[left]
12         if(arr[center] < arr[left])
13             swap(arr, left, center);
14         if(arr[right] < arr[left])
15             swap(arr, left, right);
16         
17         //将 中间那个数放到 arr[media]
18         if(arr[center] > arr[right])
19             swap(arr, center, right);
20         
21         swap(arr, center, right-1);//尽量将大的元素放到右边--将privot放到右边, 可简化 分割操作(partition).
22         return arr[right-1];//返回中间大小的那个数
23     }
复制代码

其实,这里的“三数取中”的实现,与参考资料中提到的三数取中实现有一点不同。这是正常的,毕竟实现细节不同。如果有错误,需要自行调试。

这里提下第3-7行的两个if语句:当需要 “取中”的目标数组长度为1时,或者说 对数组中某些范围内[left, right]的元素进行“取中”时,若left=right,则根本就没有3个数,违背了“三数取中”的本意(随机地选取枢轴元素),故直接 return。

当数组中元素只有一个时,第18行会越界。为了防止这种情况,在第3-4行就先对数组长度进行判断。当数组中只有两个元素,其实就相当于 center=left,因此,程序也没问题。

 

三,分割算法的应用

给定一个数组,数组中某个元素出现的次数超过了数组大小的一半,找出这个元素。

比如输入:[2,5,4,4,5,5,5,6,5] ,输出 5

这个问题,其实可以转化成求解中位数问题。因为,当数组有序时,出现次数超过一半的那个元素一定位于数组的中间。

所谓中位数,就是 假设 数组是有序的情况下,中间那个元素。即 arr[arr.length/2]

而要求解中位数,当然可以先对数组进行排序,但排序的时间复杂度为O(NlogN),那有没有更快的算法?

当然是有的。就是借助partition分割算法 来 实现。

复制代码
 1 //找出 arr 中 第  n/2  大的那个元素
 2     public static int media_number(int[] arr){
 3         int left = 0;
 4         int right = arr.length - 1;
 5         int center = (left + right) / 2;
 6         
 7         int pivot_index = parition(arr, left, right);//枢轴元素的索引
 8         
 9         while(pivot_index != center)
10         {
11             if(pivot_index > center){
12                 right = pivot_index - 1;
13                 pivot_index = parition(arr, left, right);
14             }
15             else{
16                 left = pivot_index + 1;
17                 pivot_index = parition(arr, left, right);
18             }
19         }
20         return arr[center];
21     }
复制代码

上面算法不仅可以求解“找出超过一半的数字”,也可以求解任何一个数组的中位数。

这里递归表达式 T(N)=T(N/2)+O(N),O(N)表示将数组 分成两部分所花的代价。

故时间复杂度为O(N)

 

四,参考资料

排序算法总结之快速排序

 整个完整代码

复制代码
public class Middle_Large {
    
    //找出 arr 中 第  n/2  大的那个元素
    public static int media_number(int[] arr){
        int left = 0;
        int right = arr.length - 1;
        int center = (left + right) / 2;
        
        int pivot_index = parition(arr, left, right);
        
        while(pivot_index != center)
        {
            if(pivot_index > center){
                right = pivot_index - 1;
                pivot_index = parition(arr, left, right);
            }
            else{
                left = pivot_index + 1;
                pivot_index = parition(arr, left, right);
            }
        }
        return arr[center];
    }
    
    //分割数组,将数组分成两部分. 一部分比pivot(枢轴元素)大,另一部分比pivot小
    private static int parition(int[] arr, int left, int right){
        
        int pivot = media3(arr, left, right);
        int i = left;
        int j = right - 1;//注意 ,在 media3()中 arr[right-1]就是 pivot
        
        //应对特殊情况下的数组,比如数组长度 小于3
        if(i >= j)
            return i;
        
        for(;;)
        {
            while(arr[++i] < pivot){}
            while(arr[--j] > pivot){}
            if(i < j)
                swap(arr, i, j);
            else
                break;
        }
        
        swap(arr, i, right-1);//restore pivot 将枢轴元素放置到合适位置:arr左边元素都比pivot小,右边都比pivot大
        return i;// 返回 pivot的 索引
    }
    
    
    //三数取中,用在快排中随机选择枢轴元素时
    private static int media3(int[] arr, int left, int right){
        if(arr.length == 1)
            return arr[0];
        
if(left == right)
return arr[left];
int center = (left + right) / 2; //找出三个数中的最小值放到 arr[left] if(arr[center] < arr[left]) swap(arr, left, center); if(arr[right] < arr[left]) swap(arr, left, right); //将 中间那个数放到 arr[media] if(arr[center] > arr[right]) swap(arr, center, right); swap(arr, center, right-1);//尽量将大的元素放到右边--将privot放到右边, 可简化 分割操作(partition). return arr[right-1];//返回中间大小的那个数 } private static void swap(int[] arr, int left, int right){ int tmp = arr[left]; arr[left] = arr[right]; arr[right] = tmp; } public static void main(String[] args) { int[] arr = {5,6,8,4,1,5,5,5,5}; int result = media_number(arr); System.out.println(result); } }
复制代码本文转自hapjin博客园博客,原文链接:http://www.cnblogs.com/hapjin/p/5587014.html,如需转载请自行联系原作者
相关文章
|
19天前
|
存储 负载均衡 算法
基于 C++ 语言的迪杰斯特拉算法在局域网计算机管理中的应用剖析
在局域网计算机管理中,迪杰斯特拉算法用于优化网络路径、分配资源和定位故障节点,确保高效稳定的网络环境。该算法通过计算最短路径,提升数据传输速率与稳定性,实现负载均衡并快速排除故障。C++代码示例展示了其在网络模拟中的应用,为企业信息化建设提供有力支持。
44 15
|
3天前
|
分布式计算 并行计算 算法
MapReduce在实现PageRank算法中的应用
总结来说,在实现PageRank算法时使用MapReduce能够有效地进行大规模并行计算,并且具有良好的容错性和可扩展性。
105 76
|
1天前
|
人工智能 API 开发者
HarmonyOS Next~鸿蒙应用框架开发实战:Ability Kit与Accessibility Kit深度解析
本书深入解析HarmonyOS应用框架开发,聚焦Ability Kit与Accessibility Kit两大核心组件。Ability Kit通过FA/PA双引擎架构实现跨设备协同,支持分布式能力开发;Accessibility Kit提供无障碍服务构建方案,优化用户体验。内容涵盖设计理念、实践案例、调试优化及未来演进方向,助力开发者打造高效、包容的分布式应用,体现HarmonyOS生态价值。
53 27
|
2天前
|
数据采集 机器学习/深度学习 存储
可穿戴设备如何重塑医疗健康:技术解析与应用实战
可穿戴设备如何重塑医疗健康:技术解析与应用实战
27 4
|
12天前
|
算法
一次推理,实现六大3D点云分割任务!华科发布大一统算法UniSeg3D,性能新SOTA
华中科技大学研究团队提出了一种名为UniSeg3D的创新算法,该算法通过一次推理即可完成六大3D点云分割任务(全景、语义、实例、交互式、指代和开放词汇分割),并基于Transformer架构实现任务间知识共享与互惠。实验表明,UniSeg3D在多个基准数据集上超越现有SOTA方法,为3D场景理解提供了全新统一框架。然而,模型较大可能限制实际部署。
48 15
|
7天前
|
监控 算法 安全
基于 C# 的内网行为管理软件入侵检测算法解析
当下数字化办公环境中,内网行为管理软件已成为企业维护网络安全、提高办公效率的关键工具。它宛如一位恪尽职守的网络守护者,持续监控内网中的各类活动,以确保数据安全及网络稳定。在其诸多功能实现的背后,先进的数据结构与算法发挥着至关重要的作用。本文将深入探究一种应用于内网行为管理软件的 C# 算法 —— 基于二叉搜索树的入侵检测算法,并借助具体代码例程予以解析。
23 4
|
13天前
|
人工智能 自然语言处理 供应链
从第十批算法备案通过名单中分析算法的属地占比、行业及应用情况
2025年3月12日,国家网信办公布第十批深度合成算法通过名单,共395款。主要分布在广东、北京、上海、浙江等地,占比超80%,涵盖智能对话、图像生成、文本生成等多行业。典型应用包括医疗、教育、金融等领域,如觅健医疗内容生成算法、匠邦AI智能生成合成算法等。服务角色以面向用户为主,技术趋势为多模态融合与垂直领域专业化。
|
12天前
|
JavaScript 前端开发 算法
JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)
Array.sort() 是一个功能强大的方法,通过自定义的比较函数,可以处理各种复杂的排序逻辑。无论是简单的数字排序,还是多字段、嵌套对象、分组排序等高级应用,Array.sort() 都能胜任。同时,通过性能优化技巧(如映射排序)和结合其他数组方法(如 reduce),Array.sort() 可以用来实现高效的数据处理逻辑。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
12天前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
15天前
|
人工智能 自然语言处理 算法
DeepSeek大模型在客服系统中的应用场景解析
在数字化浪潮下,客户服务领域正经历深刻变革,AI技术成为提升服务效能与体验的关键。DeepSeek大模型凭借自然语言处理、语音交互及多模态技术,显著优化客服流程,提升用户满意度。它通过智能问答、多轮对话引导、多模态语音客服和情绪监测等功能,革新服务模式,实现高效应答与精准分析,推动人机协作,为企业和客户创造更大价值。
122 5

推荐镜像

更多