快速排序由C. A. R. Hoare在1962年提出,它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。(来自度娘)

这篇博文从以下方面讲解快速排序:

  1. 快速排序基本思想
  2. 基础代码实现
  3. 一次优化
  4. 二次优化(随机快速排序)
  5. 三次优化(双路快速排序)
  6. 四次优化(三路快速排序)

1.快速排序基本思想

* (4)快速排序:从小到大排序
* 1.选定一个元素
* 2.对其他元素进行遍历,使得一部分的区域都小于这个元素的值,而另一部分则大于等于它,但这两个部分不需要有序
* 3.对这两部分再进行快速排序的步骤,知道最后有序.

2.基础代码实现

package com.sort;
public class QuickSort {static int[] quickSort(int[] arr) {return __quickSort(arr, 0, arr.length - 1);
    }// arr[left .... right] 的部分进行操作
    private static int[] __quickSort(int[] arr, int left, int right) {if (right <= left) {return arr;
        }int k = arr[left];  //选定第一个元素,作为切分两个部分的int j = left;   //作为标识 小于k 的那部分的结尾的下标
        //现在通过遍历整个数组,需要使得 arr[left ... j] 都 小于k , arr[j+1 ... right] 都 大于k ;
        for (int i = left + 1; i <= right; i++) {if (arr[i] >= k) {//如果发现 arr[i] >= k ,证明它属于第二部分,就让它待在遍历过的末尾,不要管它就好

            } else {//如果发现 arr[i] < k ,证明它属于第一部分,需要把它(arr[i]的元素值) arr[j + 1] 交换位置 ,并且将 j++,这样就保证这两部分满足规则
                int temp = arr[i];
                arr[i] = arr[j + 1];
                arr[j + 1] = temp;
                j++;
            }}//遍历结束后,把作为标识的 k 恢复到原数组中,即将 k 的元素值与 arr[j] 交换位置
        int temp = arr[left];
        arr[left] = arr[j];
        arr[j] = temp;

        //递归的对两个部分都进行遍历
        __quickSort(arr, left, j);
        __quickSort(arr, j + 1, right);
        return arr;
    }
}

3.一次优化

快速排序算法最差的情况就是近乎有序的情况,因此在近乎有序的情况下使用插入排序算法会更好,而无须递归的切分到底.

private static int[] __quickSort(int[] arr, int left, int right) {//(优化)        if (right <= left) {
//(优化)            return arr;
//(优化)        }


        /*优化1: 上面的if代码块*/
        /*在底层时近乎有序的概率很大,此时可以使用这种情况下效率较高的插入排序*/
        if (right - left <= 10) {InsertionSort.insertionSort_3(arr);
            return arr;
        }
    ........

4.二次优化(随机化快速排序算法)

//(优化)        int k = arr[left];  //选定第一个元素,作为切分两个部分的标识
        /*优化2*/
        /*如果整个数组近乎有序,那么选择第一个元素作为切分标识会导致整个切分近乎成为列表形式,时间复杂度退化为 O( n * n )
         *如果随机选取,则会使整个排序的数学期望为 O( n * log n) **/
        int k = arr[(int) (Math.random() * (right - left) + left)];  //使得k随机生成
        //k移动到最左侧
        int temp = k;
        k = arr[left];
        arr[left] = temp;

此时,整个代码为:

    static int[] quickSort(int[] arr) {return __quickSort(arr, 0, arr.length - 1);
    }// arr[left .... right] 的部分进行操作
    private static int[] __quickSort(int[] arr, int left, int right) {//(优化)        if (right <= left) {
//(优化)            return arr;
//(优化)        }


        /*优化1: 上面的if代码块*/
        /*在底层时近乎有序的概率很大,此时可以使用这种情况下效率较高的插入排序*/
        if (right - left <= 10) {InsertionSort.insertionSort_3(arr);
            return arr;
        }//(优化)        int k = arr[left];  //选定第一个元素,作为切分两个部分的标识

        /*优化2*/
        /*如果整个数组近乎有序,那么选择第一个元素作为切分标识会导致整个切分近乎成为列表形式,时间复杂度退化为 O( n * n )
         *如果随机选取,则会使整个排序的数学期望为 O( n * log n) **/
        int k = arr[(int) (Math.random() * (right - left) + left)];  //使得k随机生成
        //k移动到最左侧
        int temp = k;
        k = arr[left];
        arr[left] = temp;

        int j = left;   //作为标识 小于k 的那部分的结尾的下标
        //现在通过遍历整个数组,需要使得 arr[left ... j] 都 小于k , arr[j+1 ... right] 都 大于k ;
        for (int i = left + 1; i <= right; i++) {if (arr[i] >= k) {//如果发现 arr[i] >= k ,证明它属于第二部分,就让它待在遍历过的末尾,不要管它就好

            } else {//如果发现 arr[i] < k ,证明它属于第一部分,需要把它(arr[i]的元素值) arr[j + 1] 交换位置 ,并且将 j++,这样就保证这两部分满足规则
                int temp_1 = arr[i];
                arr[i] = arr[j + 1];
                arr[j + 1] = temp_1;
                j++;
            }}//遍历结束后,把作为标识的 k 恢复到原数组中,即将 k 的元素值与 arr[j] 交换位置
        temp = arr[left];
        arr[left] = arr[j];
        arr[j] = temp;

        //递归的对两个部分都进行遍历
        __quickSort(arr, left, j);
        __quickSort(arr, j + 1, right);
        return arr;
    }

5.三次优化(双路快速排序)

本次改变了快速排序的设计思路,采用 双路快速排序,但是测试运行时间后,会发现双路快速排序的执行时间要比原来的时间更长一些

    static int[] quickSort(int[] arr) {return __quickSort_2(arr, 0, arr.length - 1);
    }/*改进3:双路快速排序*/
/*如果存在大量重复的值(比如 10个1,100个2,1000个3 随机打乱后进行排序),那么会使小于k的部分特别少,而大于等于k的部分却很多,因此需要改进
 *  此时不再从左到右的进行遍历,而是从左右两个方向从中间遍历,一部分只搜集小于k的元素,另一部分只搜集大于k的元素.
 *  当两个方向都遇到不属于自己这部分的元素时,交换它们两个元素的位置即可.直到这两部分相遇,然后将k放回数组中,进行递归.
 * */
private static int[] __quickSort_2(int[] arr, int left, int right) {if (right - left <= 10) {InsertionSort.insertionSort_3(arr);
        return arr;
    }int k = arr[left];   //选择分割的标识
    int i = left + 1;   //使得 i 扫过的部分 <= k
    int j = right;  //使得 j 扫过的部分 >= k
    //现在需要使arr[left + 1 ... j] <= k , arr[j + 1 ... right] >= k
    //因为无法找到 i  j 暂停的具体位置,因此只能通过while的条件来进行判断
    while (true) {while (i <= right && arr[i] < k) {i++;
            //退出while时表明 arr[i] 不属于 <k 的部分
        }while (j >= left + 1 && arr[j] > k) {j--;
            //退出while时表明 arr[j] 不属于 >k 的部分
        }//退出整个循环的条件,表明当前部分排序完成
        if (i > j) {break;
        }//交换 arr[i]  arr[j] 的位置,使得它们回归属于它们的位置
        int temp = arr[j];
        arr[j] = arr[i];
        arr[i] = temp;
        i++;
        j--;
    }//完成排序后,交换 k  arr[j] 的位置
    int temp = arr[j];
    arr[j] = arr[left];
    arr[left] = temp;

    __quickSort_2(arr, left, j);
    __quickSort_2(arr, j + 1, right);
    return arr;
}

6.三路快速排序

/*优化4 : 三路快速排序*//*以k为标识,将数组分为三个部分,一部分 <k ,一部分 =k , 一部分 >k .*在双路快速排序的基础上,通过索引 i 进行整个数组的遍历,*  如果 arr[i] < i,那么就把它加入到 <k 的部分(arr[left + 1 ... lt]),*  如果 arr[i] = i,那么就把它加入到 =k 的部分(arr[lt + 1 ... gt - 1])*  如果 arr[i] > i,那么就把它加入到 >k 的部分(arr[gt ... right])*得到三个部分之后,将 k 放回应该在的位置,即与 arr[lt] 交换位置*现在得到三个部分:*  >arr[left ... lt - 1] 的部分都是 小于k 的,继续递归的快排*  >arr[lt ... gt] 的部分都是 =k 的,不需要动了*  >arr[gt ... right] 的部分都是 >k 的,继续递归的快排**  (当数组中重复的数字相当多的时候,优势会更加明显)* */private static int[] __quickSort_3(int[] arr, int left, int right) {if (right - left <= 10) {//对小于10个元素的部分进行插入排序,以提高效率InsertionSort.insertionSort_3(arr);return arr;}int k = arr[(int) (Math.random() * (right - left) + left)];  //使得k随机生成//将k移动到最左侧int temp = k;k = arr[left];arr[left] = temp;int lt = left;  //arr[left + 1 ... lt] < vint gt = right + 1; //arr[gt ... right] > vint i = left + 1;   //arr[lt + 1 ... i) = vwhile (i < gt) {if (arr[i] < k) {//小于k , 此时交换 arr[i] 与 arr[lt + 1]int t = arr[i];arr[i] = arr[lt + 1];arr[lt + 1] = t;lt++;i++;} else if (arr[i] > k) {//大于k , 此时交换 arr[i] 与 arr[gt - 1]int t = arr[i];arr[i] = arr[gt - 1];arr[gt - 1] = t;gt--;} else {//=k , 此时不变,让它保持原位置i++;}}//整个数组分割为三个部分之后,这时要把 k (arr[left]) 放置到 =k 的那部分中temp = arr[left];arr[left] = arr[lt];arr[lt] = temp;__quickSort_3(arr, left, lt - 1);__quickSort_3(arr, gt, right);return arr;}

排序算法(4)----快速排序相关推荐

  1. 十大排序算法之快速排序(两种方法)

    十大排序算法之快速排序 本文采用Java书写选择排序,其他语言类似可以借鉴着写 思想:在待排序序列中选择一个分割元素,将待排序序列中所有比分割元素关键字小的元素移动到分割元素左侧位置:将待排序序列中所 ...

  2. 排序算法之----快速排序(快速上手快速排序)

    排序算法之----快速排序(快速上手快速排序) 何为快速排序算法? 快速排序的基本思想又是什么? 其实很简单: 快速排序的基本思想是 1.先从数列中取出一个数作为基准数(这里我们的算法里面取数组最右边 ...

  3. php1到5000排序,常用的排序算法(一)--快速排序(PHP实现)

    常用的排序算法系列 快速排序 假设当前需要从小到大进行排序,快速排序的核心思路是,从当前数组中,找到一个元素作为基准比较值(key),分别从两个方向进行比较.从后往前找,比key小元素放在数组前面.然 ...

  4. 排序算法(5)快速排序

    排序算法(5)快速排序 思想:递归,分治法. 1.先从数列中取出一个数作为基准数. 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边. 3.再对左右区间重复第二步,直到各区 ...

  5. 【排序算法】快速排序(C语言)

    [排序算法]-- 快速排序 目录 一.快速排序的单趟排序 1. 霍尔法 2. 挖坑法 3. 前后指针 二.快速排序 1. 排序步骤 2. 排序完整步骤图 3. 快速排序代码 3.1 递归实现 3.2 ...

  6. 快速排序算法_常用排序算法之快速排序

    点击上方蓝色 "铁匠学编程" 关注我,让我们一起学习! 前天给大家分享了归并排序,但是它不是原地排序算法,需要消耗额外的内存空间,今天给大家分享的是江湖无人不知无人不晓的" ...

  7. 排序算法-04快速排序(Python实现)

    快速排序 性质 一种基本的交换排序算法,比较常用的排序算法,简称快排. 基本的排序思路如下.基本思想为:通过一趟排序将要排序的数据分割成独立的两部分,分割点左边的数都比它小,分割点右边的数都比它大,然 ...

  8. 程序填充(指针):3数排序_排序算法之快速排序,它为什么这么快?

    本文将介绍排序算法中最常用,以及最重要的快速排序. 1 快速排序实例 快速排序由C. A. R. Hoare在1960年提出,是冒泡排序的一种改进.快速排序就跟它的名字一样,效率很快.跟冒泡排序,选择 ...

  9. 常用排序算法(二)快速排序

    快速排序 概要 本章介绍排序算法中的快速排序. 目录 1. 快速排序介绍 2. 快速排序图文说明 3. 快速排序的时间复杂度和稳定性 4. 快速排序实现 4.1 快速排序C实现 4.2 快速排序C++ ...

  10. 排序算法之快速排序详解

    一.算法介绍 快速排序:快速排序的基本思想是通过一次排序将等待的记录分成两个独立的部分,其中一部分记录的关键字小于另一部分的关键字.C部分的快速排序一直持续到整个序列被排序. 任取一个元素 (如第一个 ...

最新文章

  1. mysql delete 会锁表吗_MySQL高压缩引擎TokuDB 揭秘
  2. nginx图片过滤处理模块http_image_filter_module
  3. Android 起调系统功能,打开系统浏览器,拨打电话,发送短信,手机震动,跳转到设置通知开关页面
  4. java udp 同一个端口实现收发_Java网络编程之UDP协议
  5. java 从已知日期计算干支纪日_两个日期计算
  6. UnityShader28:噪声纹理
  7. Lintcode 553. 炸弹袭击 题解
  8. qt三维曲线_Qt 的许可类型、主要版本以及安装步骤
  9. ExecutorService中submit和execute的区别
  10. 网关和路由器有什么区别
  11. 一例智能网卡(mellanox)的网卡故障分析
  12. rdmsr获取Intel CPU温度
  13. 音乐系统(译码作曲)
  14. 蜀门linux服务端架设,蜀门1296LINUX私服服务端(红旗6架设视频+工具)
  15. 自动发货-用千牛如何做到发货号自动转接人工号
  16. ftp安装包windows版_连接远程Linux系统的免费SSH与FTP软件介绍
  17. CoolPad 8190工程模式
  18. HTML5期末大作业:汉堡美食网站设计——餐饮美食-汉堡美食(6页) HTML+CSS+JavaScript 汉堡美食 咖啡主题HTM5网页设计作业成品
  19. 公众号滑动图代码_公众号怎么制作图片滑动的效果?怎么做可以上下滑动的长图?...
  20. Django:将有存量数据的自定义的用户表无痛继承自带的AbsUser

热门文章

  1. c++ 结构体初始化_单片机C语言 - 基于结构体的面向对象编程技巧
  2. 服务器操作系统类型怎么查,服务器查看操作系统类型
  3. 裁剪平面ClipPlane
  4. 微异构Embree照片级光线追踪解决方案
  5. 马行走路线的测试用例设计
  6. 允许匿名用户访问VisualSVN
  7. 关于CAS服务器磁盘占用的问题,锁定目录惹的祸
  8. #region的快捷键+++从一个页面中弹出一个新窗口,当新窗口关闭时刷新原窗口!...
  9. php分块查找,索引查找(索引查找、分块查找) C语言实现
  10. python爬取网页上的特定链接_python 用bs4解析网页后,如何循环打开爬取出来的网址链接?...