zoukankan      html  css  js  c++  java
  • 高速排序——JAVA实现(图文并茂)

    高快省的排序算法

    有没有既不浪费空间又能够快一点的排序算法呢?那就是“高速排序”啦!

    光听这个名字是不是就认为非常高端呢。

    如果我们如今对“6 1 2 7 9 3 4 5 10 8”这个10个数进行排序。

    首先在这个序列中随便找一个数作为基准数(不要被这个名词吓到了,就是一个用来參照的数,待会你就知道它用来做啥的了)。为了方便,就让第一个数6作为基准数吧。接下来,须要将这个序列中所有比基准数大的数放在6的右边,比基准数小的数放在6的左边,相似以下这种排列:

    3 1 2 5 4 6 9 7 10 8

    在初始状态下。数字6在序列的第1位。我们的目标是将6挪到序列中间的某个位置,如果这个位置是k。如今就须要寻找这个k。而且以第k位为分界点,左边的数都小于等于6。右边的数都大于等于6。

    想一想。你有办法能够做到这点吗?

    排序算法显神威

    方法事实上非常easy:分别从初始序列“6 1 2 7 9 3 4 5 10 8”两端開始“探測”。先从找一个小于6的数,再从找一个大于6的数,然后交换他们。这里能够用两个变量i和j。分别指向序列最左边和最右边。

    我们为这两个变量起个好听的名字“哨兵i”和“哨兵j”。

    刚開始的时候让哨兵i指向序列的最左边(即i=1)。指向数字6。

    让哨兵j指向序列的最右边(即=10),指向数字。

    这里写图片描写叙述
    首先哨兵j開始出动。

    由于此处设置的基准数是最左边的数。所以须要让哨兵j先出动,这一点非常重要(请自己想一想为什么)。

    哨兵j一步一步地向左挪动(即j–),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即i++),直到找到一个数大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。
    这里写图片描写叙述
    这里写图片描写叙述
    如今交换哨兵i和哨兵j所指向的元素的值。交换之后的序列例如以下:

    6 1 2 5 9 3 4 7 10 8
    这里写图片描写叙述
    这里写图片描写叙述
    到此。第一次交换结束。接下来開始哨兵j继续向左挪动(再友情提醒,每次必须是哨兵j先出发)。他发现了4(比基准数6要小,满足要求)之后停了下来。哨兵i也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。此时再次进行交换,交换之后的序列例如以下:

    6 1 2 5 4 3 9 7 10 8

    第二次交换结束,“探測”继续。哨兵j继续向左挪动,他发现了3(比基准数6要小,满足要求)之后又停了下来。哨兵i继续向右移动,糟啦!此时哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。

    说明此时“探測”结束。我们将基准数6和3进行交换。

    交换之后的序列例如以下:

    3 1 2 5 4 6 9 7 10 8

    这里写图片描写叙述
    这里写图片描写叙述
    这里写图片描写叙述

    到此第一轮“探測”真正结束。

    此时以基准数6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。回想一下刚才的过程,事实上哨兵j的使命就是要找小于基准数的数,而哨兵i的使命就是要找大于基准数的数,直到i和j碰头为止。

    OK,解释完成。如今基准数6已经归位。它正优点在序列的第6位。此时我们已经将原来的序列,以6为分界点拆分成了两个序列,左边的序列是“3 1 2 5 4”。右边的序列是“9 7 10 8”。接下来还须要分别处理这两个序列。由于6左边和右边的序列眼下都还是非常混乱的。

    只是不要紧,我们已经掌握了方法,接下来仅仅要模拟刚才的方法分别处理6左边和右边的序列就可以。如今先来处理6左边的序列现吧。

    左边的序列是“3 1 2 5 4”。

    请将这个序列以3为基准数进行调整,使得3左边的数都小于等于3,3右边的数都大于等于3。

    好了開始动笔吧

    如果你模拟的没有错,调整完成之后的序列的顺序应该是:

    2 1 3 5 4

    OK。如今3已经归位。接下来须要处理3左边的序列“2 1”和右边的序列“5 4”。对序列“2 1”以2为基准数进行调整,处理完成之后的序列为“1 2”。到此2已经归位。

    序列“1”仅仅有一个数,也不须要进行不论什么处理。至此我们对序列“2 1”已所有处理完成,得到序列是“1 2”。序列“5 4”的处理也仿照此方法,最后得到的序列例如以下:

    1 2 3 4 5 6 9 7 10 8

    对于序列“9 7 10 8”也模拟刚才的过程。直到不可拆分出新的子序列为止。终于将会得到这种序列,例如以下

    1 2 3 4 5 6 7 8 9 10

    到此。排序全然结束。

    细心的同学可能已经发现。高速排序的每一轮处理事实上就是将这一轮的基准数归位,直到所有的数都归位为止。排序就结束了。

    以下上个霸气的图来描写叙述下整个算法的处理过程。
    这里写图片描写叙述

    这是为什么呢?

    高速排序之所比較快。由于相比冒泡排序,每次交换是跳跃式的。每次排序的时候设置一个基准点,将小于等于基准点的数所有放到基准点的左边。将大于等于基准点的数所有放到基准点的右边。这样在每次交换的时候就不会像冒泡排序一样每次仅仅能在相邻的数之间进行交换,交换的距离就大的多了。因此总的比較和交换次数就少了。速度自然就提高了。当然在最坏的情况下,仍可能是相邻的两个数进行了交换。

    因此高速排序的最差时间复杂度和冒泡排序是一样的都是O(N2),它的平均时间复杂度为O(NlogN)。事实上高速排序是基于一种叫做“二分”的思想。我们后面还会遇到“二分”思想,到时候再聊。

    先上代码,例如以下

    代码实现:

    public class QuickSort {
        public static void quickSort(int[] arr,int low,int high){
            int i,j,temp,t;
            if(low>high){
                return;
            }
            i=low;
            j=high;
            //temp就是基准位
            temp = arr[low];
    
            while (i<j) {
                //先看右边,依次往左递减
                while (temp<=arr[j]&&i<j) {
                    j--;
                }
                //再看左边,依次往右递增
                while (temp>=arr[i]&&i<j) {
                    i++;
                }
                //如果满足条件则交换
                if (i<j) {
                    t = arr[j];
                    arr[j] = arr[i];
                    arr[i] = t;
                }
    
            }
            //最后将基准为与i和j相等位置的数字交换
             arr[low] = arr[i];
             arr[i] = temp;
            //递归调用左半数组
            quickSort(arr, low, j-1);
            //递归调用右半数组
            quickSort(arr, j+1, high);
        }
    
    
        public static void main(String[] args){
            int[] arr = {10,7,2,4,7,62,3,4,2,1,8,9,19};
            quickSort(arr, 0, arr.length-1);
            for (int i = 0; i < arr.length; i++) {
                System.out.println(arr[i]);
            }
        }
    }
    输出为
    1
    2
    2
    3
    4
    4
    7
    7
    8
    9
    10
    19
    62
    

    原文地址:http://developer.51cto.com/art/201403/430986.htm

  • 相关阅读:
    JUC强大的辅助类讲解--->>>CyclicBarrier(信号灯)
    JUC强大的辅助类讲解--->>>CyclicBarrier(循环栅栏)
    JUC强大的辅助类讲解--->>>CountDownLatchDemo (减少计数)
    FutureTask 概念及其相关使用
    集合不安全之 ---> Map
    集合不安全之 ---> Set
    180623-SpringBoot之logback配置文件
    180621-一个简单的时间窗口设计与实现
    180620-mysql之数据库导入导出
    180619-Yaml文件语法及读写小结
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8425312.html
Copyright © 2011-2022 走看看