快速排序

快速排序


1.快排原理

	基本算法思想是分治算法。
	通过一轮的排序将序列分割成独立的两部分,其中一部分序列的关键字均比另一部分关键字小。继续对长度较短的序列进
行同样的分割,最后到达整体有序。
	在排序过程中,由于已经分开的两部分的元素不需要进行比较,故减少了比较次数,降低了排序时间。
1.1详细描述
	先在要排序的序列 a 中选取一个基准数(基准数,可以随机取,也可以取固定位置,本文就取第一个为基准数吧,不要
被这个名词吓到了,就是一个用来参照的数),经过一趟排序将序列分成两个部分,其中左边的部分 b 中的元素均小于基
准数,右边的部分 c 的元素均大于基数,然后通过递归调用快速排序分别对两个部分进行排序,直到序列为空或者只有一
个元素,得到最终的有序序列。

2.例子

	例子就从网上找一个吧。
	假设我们现在对“6  1  2 7  9  3  4  5 10  8”这个10个数进行排序。首先在这个序列中随便找一个数作为基准数。
为了方便,就让第一个数6作为基准数吧。
	在初始状态下,数字6在序列的第1位。我们的目标是将6挪到序列中间的某个位置,假设这个位置是k。现在就
需要寻找这个k,并且以第k位为分界点,左边的数都小于等于6,右边的数都大于等于6。想一想,你有办法可以
做到这点吗?
	方法其实很简单
	
	1.分别从初始序列“6  1  2  7  9  3  4  5 10  8”两端开始“探测”。先从右往左找一个小于6的数,再从左往右找一
个大于6的数,然后交换他们。这里可以用两个变量i和j,分别指向序列最左边和最右边。我们为这两个变量起个
好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向序列的最左边(即i=0),指向数字6。让哨兵j指向序列的
最右边(即j=9),指向数字8。		

img1

	首先哨兵j开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动,这一点非常重要(请自己想一想为
什么)。哨兵j一步一步地向左挪动(即j--),直到找到一个小于6的数停下来。
	 
	2.接下来哨兵i再一步一步向右挪动(即i++),直到找到一个数大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停
在了数字7面前。

img2
img3

	现在交换哨兵i和哨兵j所指向的元素的值。交换之后的序列如下。

6 1 2 5 9 3 4 7 10 8

	3.第一次交换结束。接下来开始哨兵j继续向左挪动(再友情提醒,每次必须是哨兵j先出发)。他发现了4(比基准数6要
小,满足要求)之后停了下来。哨兵i也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。此时再
次进行交换,交换之后的序列如下。

img4
img5

6 1 2 5 4 3 9 7 10 8


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

img6
img7
img8

3 1 2 5 4 6 9 7 10 8


	到此第一轮“探测”真正结束。此时以基准数6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。
回顾一下刚才的过程,其实哨兵j的使命就是要找小于基准数的数,而哨兵i的使命就是要找大于基准数的数,
直到i和j碰头为止。剩下的步骤就是重复上面的过程。

	现在基准数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


3.代码实现

3.1实现方式一(上边的例子实现方法)
	/**
     * 快速排序
     * @param a
     * @param i
     * @param j
     */
    public static void quick_sort(int[] a,int i,int j){
        if(i>j){return;}
        //定义两个哨兵 start和 end
        int start = i;
        int end = j;
        //基准数
        int key = a[i];
        int t;
        while(end>start){
            int temp;
            //先从后往前比较,如果没有比基准数小的,依次往前一个数,直到有比基准数小的
            while(end>start && a[end]>=key){end--;}
            //从前往后比较,如果没有比基准数大的,依次往下一个数,直到有比基准数大的
            while(end>start && a[start]<=key){start++;}

            if(start<end){
                temp=a[start];
                a[start]=a[end];
                a[end]=temp;
            }
        }
        //最终将基准数归位
        t=a[i];
        a[i]=a[start];
        a[start]=t;

//        for(int l = 0; l<a.length; l++){
//            System.out.print(a[l]+" ");
//        }
//        System.out.println();

        //此时一次循环比较结束,基准数的位置已经确定了。左边的值都比基准数小,
        //右边的值都比基准数大,但是两边的顺序还有可能不一样,下面进行递归调用
        //左边序列,第一个索引位置到  关键值索引-1
        if(start>i) quick_sort(a,i,start-1);
        //右边序列,从  关键值索引+1  到 最后一个
        if(end<j) quick_sort(a,end+1,j);
    }
3.2实现方式二(另一种)
	/**
     * 快速排序
     * @param a
     * @param low
     * @param high
     */
    public static void quick_sort(int[] a,int low,int high){
        //定义两个哨兵 start和 end
        int start = low;
        int end = high;
        //基准数
        int key = a[low];

        while( end > start ){
            //先从后往前比较,如果没有比基准数小的,依次往前一个数,直到有比基准数小的交换位置
            while( end > start && a[end] >= key){end--;}

            if( a[end] < key ){
                int temp = a[end];
                a[end] = a[start];
                a[start] = temp;
            }

            //从前往后比较,如果没有比基准数大的,依次往下一个数,直到有比基准数大的交换位置
            while(end>start && a[start]<=key){start++;}

            if( a[start] > key ) {
                int temp = a[start];
                a[start] = a[end];
                a[end] = temp;
            }
        }
        //此时一次循环比较结束,基准数的位置已经确定了。左边的值都比基准数小,右边的值都比基准数大
        //但是两边的顺序还有可能不一样,下面进行递归调用
        //左边序列,第一个索引位置到  关键值索引-1
        if(start > low) quick_sort(a,low,start-1);
        //右边序列,从  关键值索引+1  到 最后一个
        if(end < high) quick_sort(a,end+1,high);
    }
3.3完整代码(java)(实现方式二)
	public class QuickSort {
    public static void main(String []args){
        //创建一个数组
        int[] a = {16,20,5,12,15,1,30,45,23,9};
        int start = 0;
        int end = a.length-1;

        quick_sort(a,start,end);
        System.out.println("排序后的数组:");
        for(int i = 0; i<a.length; i++){
            System.out.print(a[i]+" ");
        }
    }

    /**
     * 快速排序
     * @param a
     * @param low
     * @param high
     */
    public static void quick_sort(int[] a,int low,int high){
        //定义两个哨兵 start和 end
        int start = low;
        int end = high;
        //基准数
        int key = a[low];

        while(end > start){
            //先从后往前比较,如果没有比基准数小的,依次往前一个数,直到有比基准数小的交换位置
            while(end>start && a[end]>=key){end--;}

            if(a[end] < key){
                int temp = a[end];
                a[end] = a[start];
                a[start] = temp;
            }

            //从前往后比较,如果没有比基准数大的,依次往下一个数,直到有比基准数大的交换位置
            while(end>start && a[start]<=key){start++;}

            if(a[start] > key) {
                int temp = a[start];
                a[start] = a[end];
                a[end] = temp;
            }
        }
        //此时一次循环比较结束,基准数的位置已经确定了。左边的值都比基准数小,右边的值都比基准数大
        //但是两边的顺序还有可能不一样,下面进行递归调用
        //左边序列,第一个索引位置到  关键值索引-1
        if(start > low) quick_sort(a,low,start-1);
        //右边序列,从  关键值索引+1  到 最后一个
        if(end < high) quick_sort(a,end+1,high);
    }
}

4.时间复杂度

平均为O(nlogn)
最好为O(nlogn)
最差为O(n^2),例如,数组倒序,基准数选第一位。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值