快速排序分区以及优化方法

一、快速排序扫描分区法

  通过单向扫描,双向扫描,以及三指针分区法分别实现快速排序算法。着重理解分区的思想。

  单向扫描分区法

    思路:用两个指针将数组划分为三个区间,扫描指针(scan_pos)左边是确认小于等于主元的,扫描指针到某个指针(next_bigger_pos)中间为未知的,因此我们将第二个指针(next_bigger_pos)称为未知区间指针,末指针的右边区间为确认大于主元的元素。主元就是具体的划分数组的元素,主元的选择有讲究,这里选择数组的首元素

    代码:

import java.util.Arrays;

public class QuickSort {

    public static void main(String[] args) {
        int arr[] = new int[10];
        for(int i=0;i<10;i++){
            arr[i] = (int) ((Math.random()+1)*10);
        }
        System.out.println("排序前:"+Arrays.toString(arr));
        quickSort(arr, 0, arr.length-1);
        System.out.println("排序后:"+Arrays.toString(arr));

    }

    public static void quickSort(int[]A,int p,int r){
        if (p<r) {
            int q = partition(A,p,r);  // 得到分区返回的一个下标  以此划分数组
            quickSort(A, p, q-1);
            quickSort(A, q+1, r);
        }
    }

    private static int partition(int[] A, int p, int r) {
        int pivot = A[p];   // 主元
        int sp = p + 1;   // 扫描指针
        int bigger = r;   // 右侧指针
        while(sp<=bigger){
            if (A[sp]<=pivot) {    // 扫描元素左移,左指针向右移
                sp++;
            }else {
                swap(A,sp,bigger);    // 扫描元素大于主元,二指针的元素交换,右指针左移
                bigger--;
            }
        }
        swap(A,p,bigger);
        return bigger;
    }

    private static void swap(int[] A, int p, int bigger) {
        int temp = A[p];
        A[p] = A[bigger];
        A[bigger] = temp;

    }

}

    结果:

      

  双向扫描分区法

    思路:头尾指针往中间扫描,从左找到大于主元的元素,从右找到小于等于主元的元素二者交换,继续扫描,直到左侧无大元素,右侧无小元素。

    代码:

import java.util.Arrays;

public class QuickSort2 {

    public static void main(String[] args) {
        int arr[] = new int[10];
        for(int i=0;i<10;i++){
            arr[i] = (int) ((Math.random()+1)*10);
        }
        System.out.println(Arrays.toString(arr));
        quickSort(arr, 0, arr.length-1);
        System.out.println(Arrays.toString(arr));

    }

    public static void quickSort(int[]A,int p,int r){
        if (p<r) {
            int q = partition2(A,p,r);  // 得到分区返回的一个下标  以此划分数组
            quickSort(A, p, q-1);
            quickSort(A, q+1, r);
        }
    }

    //双向扫描分区法
    public static int partition2(int[] arr, int p, int r) {
        int left = p + 1; //左侧扫描指针
        int right = r; //右侧指针
        int pivot = arr[p];
        while(left <= right) {
            // left不停往右走,直到遇到大于主元的元素
            // 循环退出时,left一定是指向第一个大于主元的位置
            while(left <= right && arr[left] <= pivot) {
                left++;
            }
            // right不停往左走,直到遇到小于主元的元素
            // 循环退出时,right一定是指向从右到左第一个小于于主元的位置
            while(left <= right && arr[right] > pivot) {
                right--;
            }
            if(left < right)
                swap(arr, left, right);
        }
        // 循环退出时,两者交错,且right指向的最后一个小于等于主元的位置,也就是主元应该待的位置
        swap(arr, p, right);
        return right;
    }

    private static void swap(int[] A, int p, int bigger) {
        int temp = A[p];
        A[p] = A[bigger];
        A[bigger] = temp;

    }
}

    结果:

      

  三指针分区法

    思路:当待排序数组中,如果有大量相同的元素,则可以三指针分区法,每次将与主元相等的元素找到,排好序,并记录这组与主元相等元素序列的开始下标和结束下标。在进行下次递归排序时,排除这部分相同的元素。从而减少递归次数。

    代码:

import java.util.Arrays;

public class QuickSort3 {

    public static void main(String[] args) {
        int arr[] = new int[10];
        for(int i=0;i<10;i++){
            arr[i] = (int) ((Math.random()+1)*10);
        }

        System.out.println("排序前:"+Arrays.toString(arr));
        quickSort(arr, 0, arr.length-1);
        System.out.println("排序后:"+Arrays.toString(arr));

    }

    public static void quickSort(int[]A,int p,int r){
        if (p<r) {
            int q[] = partition3(A,p,r);  // 得到分区返回的一个下标  以此划分数组
            quickSort(A, p, q[0]-1);
            quickSort(A, q[1]+1, r);
        }
    }

    private static int[] partition3(int[] arr, int p, int r) {
        int s = p + 1;  //左扫描指针
        int e = s; //记录与主元相等元素序列的开始下标
        int bigger = r; //右侧扫描指针
        int pivot = arr[p]; //主元
        while (s <= bigger) {
            while(s <= bigger && arr[s] <= pivot) {
                //当从一开始没有找到与主语相等的元素,且都小于主元时,指针右移
                if(s <= bigger && s == e && arr[s] < pivot) {
                    s++;
                    e++;
                }
                //如过s!=e时,说明已经找到与主元相等的元素,且e记录的为与主元相等元素的开始下标
                //如果下一个元素小于主元,则将小于主元的元素和与主元相等序列的第一个元素交换位置
                if(s <= bigger && s != e && arr[s] < pivot) {
                    swap(arr, s, e);
                    e++;
                    s++;
                }
                //如果遇到等于主元的元素,左扫描指针++, 记录与主元相等序列的开始下标e不变
                if(s <= bigger && arr[s] == pivot) {
                    s++;
                }
            }
            //右侧扫描指针
            while(s <= bigger && arr[bigger] > pivot) {
                bigger--;
            }
            //将左侧指针指向大的元素与右侧小于主元的元素交换
            if(s <= bigger && arr[s] > arr[bigger]) {
                swap(arr, s, bigger);
            }

        }
        //最后,数组下标为p的开始元素,和与主元相等序列的前一个元素交换,e--
        swap(arr, p, --e);
        //返回与主元相等序列的开始下标和结束下标
        int[] q = {e, bigger};
        return q;
    }

    private static void swap(int[] A, int p, int bigger) {
        int temp = A[p];
        A[p] = A[bigger];
        A[bigger] = temp;

    }
}

    结果:

      

二、快速排序在工程实践中优化方法

  1、三点中值法:Arrays.sort()方法就是采用的三点中值法。在上面的例子中,每次取的主元都是待排序子序列的首元素,很大可能不是属于中间的元素,从而容易加大递归的层数。三点中值法就是对待排序数组的开始,中间,最后三个元素的大小进行比较,然后取中间值,这样很大概率能使主元成为中间的元素,从而减少递归层数。

  2、绝对中值法:三点中值法也有很大的随机性,如果想要得到绝对的中值,可以通过绝对中值法来获取主元,通过将待排序数组以5个元素分为一组,取中间值,取到整个数组的各组中间值,再将这些数排序,再取中间值作为主元。因为寻找绝对中值,也会花费时间,所以使用三点中值法居多。

  3、待排序列表较短时,用插入排序:当排序列表小于8个时,通过计算发现插入排序比快速排序的性能要好。

原文地址:https://www.cnblogs.com/xiaoyh/p/10263617.html

时间: 2024-10-13 17:46:46

快速排序分区以及优化方法的相关文章

【Spark 深入学习-08】说说Spark分区原理及优化方法

本节内容 ------------------ · Spark为什么要分区 · Spark分区原则及方法 · Spark分区案例 · 参考资料 ------------------ 一.Spark为什么要分区    分区概念:分区是RDD内部并行计算的一个计算单元,RDD的数据集在逻辑上被划分为多个分片,每一个分片称为分区,分区的格式决定了并行计算的粒度,而每个分区的数值计算都是在一个任务中进行的,因此任务的个数,也是由RDD(准确来说是作业最后一个RDD)的分区数决定. 为什么要分区,这个借用

基于页面染色技术的内存数据库访问优化方法

本发明公开了一种基于页面染色技术的内存数据库访问优化方法.该方法首先将弱局部性数据集的所有数据页面的访问顺序按页面颜色进行排序,并将所有数据页面按页面颜色进行分组,然后按页面颜色分组的顺序扫描弱局部性数据集的所有数据页面.进一步地,预设若干具有相同页面颜色的内存页面作为页面颜色队列,该页面颜色队列用作内存页面被加载入CPU缓存之前的内存缓存:弱局部性数据集的数据页面首先通过异步方式进入页面颜色队列,然后再被加载到CPU缓存中完成数据处理.本发明能够解决内存数据库应用中无法依赖页面颜色为进程.线程

20亿与20亿表关联优化方法(超级大表与超级大表join优化方法)

记得5年前遇到一个SQL.就是一个简单的两表关联.SQL跑了几乎相同一天一夜,这两个表都非常巨大.每一个表都有几十个G.数据量每一个表有20多亿,表的字段也特别多. 相信大家也知道SQL慢在哪里了,单个进程的PGA 是绝对放不下几十个G的数据,这就会导致消耗大量temp tablespace,SQL慢就是慢在temp来回来回来回...的读写数据. 遇到这样的超级大表与超级大表怎么优化呢?这篇文章将告诉你答案. 首先创建2个測试表 t1,t2 数据来自dba_objects create tabl

关于Oracle程序块(主要为sql)优化方法小结

Oracle优化本身就是一件难度比较大的事情,所涉及的事情方方面面.下面说一下我的优化经验(仅限于初学者使用): 很多书上说的优化经验,都包括索引.表结构.标量子查询以及数据库层面的优化,但是80%的优化都可以是语句级的优化.优化的对象包括:procedure.function及Sql. 对于对Oracle数据库很熟悉的人来说,优化基本不需要借助任何工具就可以做到.但下面我说两个工具用来进行Sql优化:dbms_profile和advisor两个工具. 当然也可以通过执行计划进行优化. 最后会简

redmine在linux上的mysql性能优化方法与问题排查方案

iredmine的linux服务器mysql性能优化方法与问题排查方案 问题定位: 客户端工具: 1. 浏览器inspect-tool的network timing工具分析 2. 浏览器查看 response header, 分析http server 与 web server.       服务器工具:   0. nmon 查看各类系统负载, rrdtool 查看网络状况.   1. uptime看cpu负载;    free看内存;  mem ; cat /proc/meminfo以及  i

大数据技术之_05_Hadoop学习_04_MapReduce_Hadoop企业优化(重中之重)+HDFS小文件优化方法+MapReduce扩展案例+倒排索引案例(多job串联)+TopN案例+找博客共同粉丝案例+常见错误及解决方案

第6章 Hadoop企业优化(重中之重)6.1 MapReduce 跑的慢的原因6.2 MapReduce优化方法6.2.1 数据输入6.2.2 Map阶段6.2.3 Reduce阶段6.2.4 I/O传输6.2.5 数据倾斜问题6.2.6 常用的调优参数6.3 HDFS小文件优化方法6.3.1 HDFS小文件弊端6.3.2 HDFS小文件解决方案第7章 MapReduce扩展案例7.1 倒排索引案例(多job串联)7.2 TopN案例7.3 找博客共同粉丝案例第8章 常见错误及解决方案 第6章

垃圾邮件过滤优化方法

垃圾邮件过滤优化方法 通过honeypot project 搜集大量垃圾邮件数据 通过解析邮件header 获取垃圾邮件发送路径和服务器相关信息 对编写错误的单词的修正 比如:w4tch 对相同含义的词进行归类处理,比如:discount 和discounts   (可以通过porter stemmer,下面就是该算法c语言的一种实现) /* This is the Porter stemming algorithm, coded up in ANSI C by the author. It m

【转】JCR期刊分区及其检索方法

不少机构依据JCR期刊分区制定科研激励政策,相关科研工作者及科研管理机构密切关注JCR期刊分区及其检索方法.本文作一粗略介绍.    关于JCR(Journal Citation Reports,期刊引证报告)期刊分区影响较为广泛的有两种:一是Thomson Reuters公司自身制定的分区,一是中国科学院国家科学图书馆制定的分区(简称中科院分区).它们均基于SCI收录期刊影响因子基础之上.    二者有何区别?Thomson Reuters公司本身做了分区,按Thomson Reuters的学

Linux网络性能优化方法简析

Linux网络性能优化方法简析 2010-12-20 10:56 赵军 IBMDW 字号:T | T 性能问题永远是永恒的主题之一,而Linux在网络性能方面的优势则显而易见,这篇文章是对于Linux内核中提升网络性能的一些优化方法的简析,以让我们去后台看看魔术师表演用的盒子,同时也看看内核极客们是怎样灵活的,渐进的去解决这些实际的问题. AD:2014WOT全球软件技术峰会北京站 课程视频发布 对于网络的行为,可以简单划分为 3 条路径:1) 发送路径,2) 转发路径,3) 接收路径,而网络性