分治算法之快速排序

分治算法之——快速排序算法(C++实现) 快速排序是分治策略、迭代的典型示例,需要熟练掌握。 核心思想 将数组中所有元素都跟一个基准元素x比(随意选取,常取第一个或最后一个),比x小的划分成左边一块,比x大的划分成右边一块,得到两个子问题。然后递归处理这两个子问题即可。 其关键在于对数组的划分。 代码实现 下面是C++实现代码: 代码参考:常用算法之----快速排序 #include <iostream> using nam... 阅读详情
  1. 分治算法由两部分组成:
    分:递归解决较小的问题(基本情况除外)。
    治:然后从子问题的解构建原问题的解。
  2. 传统上,在正文中至少含有两个递归调用的例程叫作分治算法,而正文中只含有一个递归调用的例程不是分治算法。一般坚持子问题是不相交的(即基本不重叠)。
  3. 分治算法的经典例子有归并排序和快速排序,它们分别有O(N logN)的最坏情形以及平均情形的时间界。
  4. 所有有效的分治算法都是把问题分成一些子问题,每个子问题都是原问题的一部分,然后进行某些附加的工作以算出最后的答案。这里我们讨论分治算法中的快速排序算法。
  5. 快速排序算法的平均运行时间是O(N logN),该算法之所以特别快,主要是由于非常精练和高度优化的内部循环。它的最坏情形性能为O(N^2),但经过稍许努力可使这种情况极难出现。通过将快速排序和堆排序结合,由于堆排序的O(N logN)最坏情形运行时间,我们可以对几乎所有的输入都能达到快速排序的快速运行时间。
  6. 简单的递归排序算法实现:
/**
 1. @author zclong
 */
public class Quicksort {

    static int count = 0; // 计数器

    public static void main(String[] args) {
        List<Integer> items = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            items.add((int) (Math.random() * 10));
        }
        System.out.println("排序前列表:" + items);
        long startTime = System.currentTimeMillis();
        sort(items); // 进行排序
        long endTime = System.currentTimeMillis();
        System.out.println("排序后列表:" + items + ",递归次数:" + count);
        System.out.println("所用时间为:" + (endTime - startTime) + "毫秒");
    }

    /**
     * 排序算法实现
     * 
     * @param items
     */
    public static void sort(List<Integer> items) {
        if (items.size() > 1) {
            count++; // 计数
            List<Integer> smaller = new ArrayList<>(); // 存储小于被选项的一组
            List<Integer> same = new ArrayList<>(); // 存储等于被选项的一组
            List<Integer> large = new ArrayList<>(); // 存储大于被选项的一组

            Integer chosenItem = items.get(items.size() / 2); // 获取列表中间项的元素(被选项)
            // 进行对列表遍历,与被选项比较
            for (Integer i : items) {
                if (i < chosenItem)
                    smaller.add(i);
                else if (i > chosenItem)
                    large.add(i);
                else
                    same.add(i);
            }

            sort(smaller); // 递归调用排序方法
            sort(large);

            items.clear();
            items.addAll(smaller);
            items.addAll(same);
            items.addAll(large);
        }
    }
}
  1. 上述排序算法实现原理:
    随机选取任意一项,如何形成三组:小于被选项的一组,等于被选项的一组,大于被选项的一组。 递归地对第一和第三组排序,然后把三组接龙。根据递归的基本原理,结果保证是对原始列表的一个有序排列 (如果表中含有大量重复项,以及相对较少的不同项,其表现是非常好的) 但是该方法会产生额外的列表,并且还是递归的这么做,我们必须避免使用大量额外的内存,并且有干净的内循环。于是快速排序通常应避免建立第二组(包含等于项的)。
  2. 经典的快速排序:其中输入存放在数组里,且算法不产生额外的数组。
    将数组S排序的基本算法有下列四步完成 :
    a,如果S中元素个数是0或者1,则返回。
    b,取S中任一元素v,称之为枢纽元(pivot)。
    c,将S-{v}(S中其余元素)划分成两个不相交的集合:S1={x∈S-{v} | x<=v}和S2={x∈S-{v} | x>=v}.
    d,返回{ quicksort(S1)后跟v,继而返回quicksort(S2) }。

  3. 以图例说明快速排序的各步:
    这里写图片描述

  4. 如同归并排序那样,快速排序递归的解决两个子问题并需要线性的附加工作(第三步),不过,与归并排序不同,这两个子问题并不保证具有相同的大小,这是一个潜在的隐患。快速排序更快地原因在于,第三步分割成两组实际上是在适当位置进行并且非常有效,他的高效不仅可以弥补大小不等的递归调用而且还能有所超出。

  5. 实现快速排序的关键是第二步和第三步:
    一、选取枢纽元:
    a,一种错误的做法是将第一个元素用作枢纽元:
    如果输入是随机的,那么这是可以接受的,而如果输入是预排序的或者是反序的,那么这样的枢纽元就产生一个劣质的分割,因为所有的元素不是被划入S1就是被划入S2。更糟糕的是这种情况毫无例外的发生在所有的递归调用中。因此使用第一个元素作为枢纽元是绝对可怕的坏主意。另一个想法是选取前两个互异的关键字中的较大者作为枢纽元,不过这和只选取第一个元素作为枢纽元具有相同的害处。不要使用这两种选取枢纽元的策略。
    b,一种安全的做法是随机选取枢纽元:
    因为随机的枢纽元不可能总在接连不断的产生劣质的分割。另一方面,随机数的生成开销很大,根本减少不了算法其余部分的平均运行时间。
    c,三数中值分割法:
    一组N个数的中指(也叫作中位数)是第N/2(上取整)个最大的数。枢纽元的最好的选择是数组的中值。不幸的是,这很难算出并且会明显减慢快速排序的速度。一般的做法是使用左端、右端和中心位置上的三个元素的中值做为枢纽元。例如,输入8,1,4,9,6,3,5,2,7,0,他的左元素是8,右元素是0,中心位置((left+right)/2)(下取整)上的元素是6。于是枢纽元则是v=6。显然使用三数中值分割法消除了预排序输入的坏情况(在这种情况下,这些分割都是一样的)。
    二、分割策略:
    分割是一种很容易出错或低效的操作,但使用一种已知的方法是安全的。
    1、该法的第一步是将枢纽元与最后的元素交换使得枢纽元离开要被分割的数据段。i从第一个元素开始而j从倒数第二个元素开始。设输入的数据为8,1,4,9,6,3,5,2,7,0,枢纽元为6。
    这里写图片描述
    2、在分割阶段要做的就是把所有小的元素移到数组的左边而把所有大元素移到数组的右边。“小”和“大”是相对于枢纽元而言的。
    当i在j的左边时,我们将i右移,移过那些小于枢纽元的元素,并将j左移,移过那些大于枢纽元的元素。当i和j停止时,i指向一个大元素而j指向一个小元素。如果i在j的左边,那么将这两个元素互换,其效果就是把一个大元素推向右边而把一个小元素推向左边。
    这里写图片描述
    3、然后我们交换由i和j指向的元素,重复该过程直到i和j彼此交错为止。
    第一次交换后

    第二次交换前
    这里写图片描述
    第二次交换后
    这里写图片描述
    第三次交换前
    这里写图片描述
    此时,i和j已经交错,故不再交换。分割的最后一步是将枢纽元与i所指向的元素交换。
    在与枢纽元交换后:
    这里写图片描述

  6. 对于很小的数组(N<=20),快速排序不如插入排序。通常解决对于小的数组不使用递归的快速排序,而取而代之的是如插入排序这样的对小数组有效的排序算法。

  7. 执行三数中值分割法的程序:

/**
     * 三数中值分割法
     * 一组N个数的中值(也叫中位数)是第N/2(下取整)个最大的数
     * @param a
     * @param left
     * @param right
     * @return
     */
    @SuppressWarnings("hiding")
    private static <Integer extends Comparable<? super Object>> Object median3(java.lang.Integer[] a, int left, int right) {
        int center = (left + right) / 2;
        if(a[center].compareTo(a[left]) < 0) {
            swapReferences(a, left, center); //交换引用
        }
        if(a[right].compareTo(a[left]) < 0) {
            swapReferences(a, left, right);
        }
        if(a[right].compareTo(a[center]) < 0) {
            swapReferences(a, center, right);
        }

        swapReferences(a, center, right-1);
        return a[right-1];
    }

    /**
     * 对列表的引用进行交换
     * @param a
     * @param arg0
     * @param arg1
     */
    private static void swapReferences(Object[] a, int arg0, int arg1) {
        Object temp;
        temp = a[arg0];
        a[arg0] = a[arg1];
        a[arg1] = temp;
    }

快速排序的主程序:

/**
     * 快速排序的主方法
     * @param a
     * @param left
     * @param right
     */
    private static <Integer extends Comparable<? super Object>> void quicksort(java.lang.Integer[] a, int left, int right) {

        // 当i和j已经交错,故不再交换
        if(left>=right) {
            return;
        }

        int pivot = (int) median3(a, left, right); // 获取枢纽元
        System.out.println("枢纽元为:" + pivot);
        /*System.out.println("快速排序后列表为:");
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + " ");
        }*/

        int i = left, j = right-1;
        while(i != j) {
            while(a[++i].compareTo(pivot) < 0) {}
            while(a[--j].compareTo(pivot) > 0) {}
            if(i < j) {
                swapReferences(a, i, j);
            }else {
                break;
            }
        }

        swapReferences(a, i, right-1);

        quicksort(a, left, i-1);
        quicksort(a, i+1, right);
    }
  1. 快速排序的最好的情况分析:当枢纽元正好位于中间。
    T(N) = 2T(N/2) + cN 可推出 T(N) = cN logN + N = O(N logN)

  2. 快速排序的最坏的情况分析:当枢纽元始终是最小元素。
    T(N) = T(N - 1) + cN 可推出 T(N) = O(N^2)

  3. 快速排序的平均时间:对于平均情况,假设对于S1,每个大小都是等可能的,因此每个大小均有概率1/N。
    推得: T(N) = O(N logN)
快速排序算法——分治法的应用 快速排序(Quicksort)算法简称快排,是学习算法和数据结构的初学者必须接触的一种经典的排序方法。这篇文章从三个部分分别介绍快速排序算法的实现、性能的分析以及算法的优化。 一、快速排序算法的伪代码实现 快速排序的三步分治过程: 首先将数组A[p....r]划分为两部分A[p..q-1]、A[q+1..r],使得左半部分中的每个元素都小于等于A[p],右半部分的元素都大于A[p]; 通过递... 阅读详情

相关推荐

在PyCharm中配置使用Anaconda环境

在PyCharm中配置使用Anaconda环境一、步骤1.Anaconda2.PyCharm总结 一、步骤 1.Anaconda 如未安装Anaconda可至其官网下载,学习使用个人版就可以了。 下载地址:https://www.anaconda.com/products/individual 1、打开Anaconda Navigator,点击Environments可以看到anaconda中已经有了默认的“base”环境,其中包含了大多数我们会用到的包,在PyCharm中可以直接使用该环境,而无需自行下载

TuckX的博客 19万+

C语言分而治之法之快速排序

分治法之快速排序 文章目录分治法之快速排序前言一、分治算法的思想二、快速排序的详解1.快速排序的思想2.快速排序的步骤三.程序代码总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇文章正文内容,下面案例可供参考 一、分治算法的思想 分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。 二、快速排序的详解 1.快速

qq_44084209的博客 2396

51单片机IAP技术详解:从原理到实战,实现远程程序自更新

在嵌入式系统开发中,程序更新是产品维护和功能迭代的核心需求。传统方式依赖外部烧录器,操作繁琐且无法远程进行。IAP(在应用中编程)技术则从原理层面解决了这一难题,它允许单片机在运行过程中通过特定接口(如串口)动态修改自身的Flash程序存储器。这项技术的核心价值在于赋予设备远程升级和自主维护的能力,极大地延长了产品生命周期并降低了维护成本。其典型应用场景包括智能家居控制器、工业数据采集模块和远程仪表等部署后不便拆卸的设备。本文以经典的51单片机(如STC89C52)为例,深入剖析IAP的实现,其中Bootl

weixin_29161719的博客 396

算法:分治法之快速排序

思路: 1.找一个支点元素pivot,一般选这组数的第一个 2.把大于pivot的数都放在它的右边,小于它的数都放在左边 3.分别对左右子序列进行1、2步操作(分治策略的体现) 示例: 19 97 09 17 01 08 Pivot ↑ ↑ L R 选择第一个元素(19)作为支点pivot,...

weixin_58346595的博客 6826

【鸿蒙南向开发】OpenHarmony音频驱动audio适配方案

这份鸿蒙(HarmonyOS NEXT)文档包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

HarmonyOS_666的博客 4507

吐血总结《Mysql从入门到入魔》,图文并茂

文章目录1. 查询1.1 查询多列1.2 检索唯一值1.3 限制输出1.4 注释2. ORDER BY 排序2.1 单列排序3.2 多列排序3.3 指定排序方向4. WHERE 过滤数据4.1 WHERE 子句操作符4.2 范围值检查4.3 空值检查5. 高级数据过滤5.1 WHERE 组合过滤5.2 IN 操作符5.3 NOT 操作符6. 通配符过滤6.1 % 通配符6.2 _ 通配符7. 创建计算字段7.1 计算字段7.2 拼接字段7.3 执行算数计算8. 函数8.1 常用文本处理函数8.2 日期和时间

向日葵的专属太阳 3万+

【算法(三·四):分治思想——快速排序】

本文介绍了快速排序算法,并分析比较了其两种实现方式(皆利用分治思想),最后比较了到目前为止讲过的所有排序算法。

计算机领域博客,与时俱进的思想与技术。 3911

高斯泼溅(Gaussian Splatting):3D建模的颠覆性革命

高斯泼溅(Gaussian Splatting):3D建模的颠覆性革命

yingtu666的博客 5128

分治算法之快速排序法

分治算法:通常我们将规模为n的原问题归为规模大概减半的子问题,分别求解每个子问题,最后进行综合,从而得到原问题的解。(子问题可以是很多个)而今天介绍的快速排序法就是一个典型的分治算法的问题。

whdsg66的博客 1052

(MATLAB)一维信号峰值检测findpeaks

一维信号波峰波谷检测,峰值、峰宽、到达时间计算。MATLAB自带的findpeaks函数能完成这一任务,在这里分享其基本用法与特殊用法。

HPC_ZY 2万+

腾讯音乐 QQ音乐商业化客户端开发工程师:岗位深度解析与面试攻略

腾讯音乐QQ音乐商业化客户端开发工程师岗位要求3年以上Android开发经验,熟练掌握Kotlin/Java及跨端开发技术。核心职责包括QQ音乐客户端功能开发、架构优化、性能提升及技术攻关。岗位要求计算机相关本科以上学历,具备完整项目开发经验,熟悉TCP/IP等网络协议,能独立解决复杂技术问题。面试重点考察项目经验、架构设计、性能优化及新技术研究能力,建议候选人准备具体案例,量化成果,并熟悉Flutter等跨端技术。该职位强调技术深度与产品思维的结合,需展现扎实的编程基础、问题解决能力和团队协作精神。

郑伟强dev的专栏 359

快排算法(分治法)

当right找到了比privot小的数我们就停止对他的移动,然后移动我们的left,同理,如果left遇到的数小于privot我们就不用管,直到遇到大于privot的数就停止。设我们接下来需要对小堆进行排序,对小堆进行排序的话也就是当前的右下标指向的值不能超过privot,我们就可以让右下表指向right-1的位置,然后再把L和right-1再传给这个函数它继续对剩下的值进行排序,当最后只有一个数的时候我们认为它就是有序的。交换之后的数组就变成了左边全是小于privot的数,右边全是大于privot的数。

Trustmeican的博客 4712

学Simulink——基于Simulink的AUTOSAR架构下电机控制软件组件建模

本文详细介绍了在Simulink中基于AUTOSAR架构开发电机控制软件组件(SWC)的全流程。主要内容包括:首先阐述了AUTOSAR架构对汽车电子系统开发的价值,包括提高软件复用率、降低集成成本和改善验证环境。然后重点讲解了在Simulink中构建电机控制SWC的具体步骤:从创建AUTOSAR组件模型、定义数据端口和服务端口,到搭建FOC控制算法、配置运行实体,最后生成AUTOSAR描述文件和验证RTE接口。文章还提供了与基础软件(BSW)的集成方案、工程实践要点和常见问题解决方案,并以大众MEB平台等实

amy_mhd的博客 61

分治算法之快速排序。

今天老师提了下快排,借此机会来复习下分治算法。希望大家能从中取得收获,如果有错误,也请大家指正。 分治算法,就是将一个大问题分成若干个小问题逐一求解。然后,这些小问题的解合并又可以得到总问题的解。 这就是分治算法的思想。 对于分治算法。比较典型的有 归并排序和快速排序和二分查找。这些都是效率挺高的算法。 在C++中,我们有头文件algorithm可以直接使用。但我们应该要把其中的过程给理清。 好好...

long_magic的博客 1012

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

快速排序分治算法解析

快速排序分治算法解析快速排序分治算法解析 声明 快速排序-分治算法思路 快速排序-划分算法Partition 1 快速排序不稳定性 2 leftIndex leq rightIndex与leftIndex rightIndex辨析 3 保证pivot切分均匀 快速排序-分治法递归实现代码 声明文章均为本人技术笔记,转载请注明出处:https://segmentfault.com/u/yzwall1

less is more 2301

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

【算法专题】分治 - 快速排序

算法专题 --- 分治 - 快速排序

YoungMLet的博客 2534

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

C语言七大排序算法之分治之快速排序算法

#C语言七大排序算法之分治 在上个星期,我看完了七大排序算法(其实是看完了除选择、冒泡、插入排序之外的四种) 而在这篇文章中,我会把快速和归并算法通过自己的理解描述出来。 快速排序算法 归并排序算法 快速排序算法到底有多快呢(来看这张表吧) ...

qq_35489187的博客 2420

算法训练之分治(快速排序)

分治(快速排序)算法入门

2401_82924480的博客 1046

分治——快速排序算法题

分治解决问题的思想就是大事化小,将一个大问题分解成若干个子问题。这道题其实就是让我们对数组进行操作,使操作之后的数组呈现出三段区间[0,left],[left+1,right-1],[right,size-1],这三个区间的元素分别都是0,1,2.既然是进行数组分块,我们就可以使用双指针的思想来解决:首先定义一个指针i;然后在分别创建两个指针和我们的目的是将数组分为三块,。

xsc2004zyj的博客 1178

分治算法(以快速排序为例)

分治算法步骤 step1: 将问题划分成同一个问题的几个较小的实例,最好拥有相同的规模。 step2: 对这些较小的实例进行求解(一般采用递归的方法,进一步对问题的实例进行划分),当问题规模足够小的时候,能够很容易地求出问题的解。 step3: 如果有必要的话,合并这些较小问题的解以得到原始问题的解。 以快速排序为例: //快速排序,start为第一个元素的位置,end为最后一个元...

u012356925的专栏 916

【分治算法】用C语言解决快速排序问题

一、分治算法要点 1、可分 2、同质 3、有终 4、无关 5、可合 二、分治算法的实现 1、递归 2、非递归 三、快速排序 1、快速排序的思想就是从n个元素中任取一个元素(通常为第1个元素))作为基准,将整个数据序列分为两个子序列,小于基准的元素放在前子序列中,大于基准的序列放在后子序列中,并把基准放在两个子序列中间,后对两个子序列反复执行,直到每个子序列为空或只有一个元素为止。 2、分治策略如下: 1)将原子序列a[s..t]分为a[s,i-1]和a[i+1,t]两个部分,i为基准,将整个问

weixin_64682807的博客 3272

分治算法-快速排序

快速排序 为了弥补上一篇线性时间选择问题的不足,你们的小周周想来想去,觉得还是讲一下快速排序比较好一点,这样大家就更加容易理解线性时间选择问题了。 快速排序的思想 快速排序也是分治递归思想,其主要是要找一个基准元(一个作为比较的元素,一般选取数组第一个元素作为基准元)出来,将数组里面的元素和他比较,用一个while()循环从左边开始找到比基准元大的元素,然后就退出;接着再用一个while()循环从右边开始找比基准元小的元素,找到就退出。然后将左边找到的元素的下标和右边找到的元素的下标进行比较,如果左边的元素

weixin_48624193的博客 1346

分治算法——快速排序(JAVA实现)

快速排序:又称划分交换排序(partition-exchange sort),简称快排,一种排序算法,最早由东尼·霍尔提出。在平均状况下,排序n个项目要O(n log2 n)(大O符号)次比较。在最坏状况下则需要 O(n^2)次比较,但这种状况并不常见。事实上,快速排序 (n log n)通常明显比其他算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地达成。简单来说:就是给基准值找正确位置的过程。

weixin_52954183的博客 874

分治法实现快速排序算法

通过算法分析来引导如何通过代码来实现心中的想法,如何去写程序,实现算法。 2.Advanced algorithm for solving 2.1 divide and merge 方法:分治法求解问题的思想是将问题中通过整体代入的思想将问题递归地分成小问题(即规模较小的原问题),并且每一次递归后都要进行合并操作,只有这样在递归结束后,分解的小问题的解就自动合并成最终的解...

大学摄影爱好者 5792

分治法之快速排序算法解题思路

快速排序算法的基本思想是:先找一个基准元素(比如待排序数组的第一个元素),进行一趟快速排序,使得该基准元素左边的所有数据都它小,而右边的所有数据都它大,然后再按此方法,对左右两边的数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数组变成有序序列。 比如我们现在对序列arr={-2,6,88,0,-4,56}这6个数进行排序: 0 1 2 3 4 5 ...

qq_32919451的博客 6694
上一篇: 用递归程序求解随机产生一个正整数n(n>=100000),确定n是否是它所有因子之和
下一篇: 在Windows服务器上安装JDK,Tomcat,MySQL进行项目的部署运行
犀利小屁孩
博客等级 码龄10年 34粉丝 12原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值