快速排序

       快速排序的基础是划分:划分就是在一组数据中,选择一个数作为枢轴,利用枢轴把小于枢轴的数排在枢轴的左边,大于枢轴的数排在枢轴右边。
       这里写图片描述
       如上图所示,枢轴将数据划分为小于枢轴的部分和大于枢轴的部分,这就是划分。

talk is cheap,show me the code:

public class Partition {
    private int[] array;
    private int n;
    public Partition(int size){
        array = new int[size];
        n = 0;
    }
    public void insert(int value){
        array[n++] = value;
    }
    public void display(){
        for(int i=0;i<n;i++){
            System.out.println(array[i]);
        }
    }
    public int size(){
        return n;
    }
    public int partitionIt(int left,int right,int pivot){
        int leftPtr = left - 1;
        int rightPtr = right + 1;
        while(true){
            while(leftPtr<right && array[++leftPtr]<pivot)
                ;
            while(rightPtr>left && array[--rightPtr]>pivot)
                ;
            if(leftPtr>=rightPtr){
                break;
            }else{
                swap(leftPtr,rightPtr);
            }
        }
        return leftPtr;
    }
    public void swap(int left,int right){
        int temp = array[left];
        array[left] = array[right];
        array[right] = temp;
    }
}

public class PartitonDemo {
    public static void main(String[] args) {
        int size = 10;
        Partition partition = new Partition(size);
        partition.insert(7);
        partition.insert(1);
        partition.insert(4);
        partition.insert(2);
        partition.insert(10);
        partition.insert(3);
        partition.insert(6);
        partition.insert(9);
        partition.insert(8);
        partition.insert(5);
        System.out.println("展示数据:");
        partition.display();
        int pivot = 6;//枢轴
        System.out.println("枢轴所在位置为:"+partition.partitionIt(0, size-1, pivot));
        System.out.println("展示数据:");
        partition.display();
    }
}

运行结果为:

展示数据:
7
1
4
2
10
3
6
9
8
5
枢轴所在位置为:6
展示数据:
5
1
4
2
6
3
10
9
8
7

       上面划分的栗子运用枢轴将数据划分为两个部分(小于枢轴的和大于枢轴的),但是在每一部分的内部,数据并不是有序的(尽管总体是比枢轴大或者小),但我们可以再对每一部分进行同样的划分,直到划分的粒度足够小(例如到1),那么此时所有的数据都是按照书序排列的啦。注意,每一次划分,其实最终枢轴的位置已经排列到正确的位置了,因为左边的都比它小,右边的都比它大,这样我们在每次划分时,就可以不考虑枢轴元素了,这样程序可以节省资源。

       基本的递归的快速排序的算法很简单,总结如下:

  • 利用枢轴将数组划分为两部分:左边部分小于枢轴,右边部分大于枢轴
  • 调用自身对左边的数据进行排序
  • 调用自身对右边的数据进行排序

       下面我们取数组的最右边的数据作为枢轴,代码如下:

public class QuickSort {
    private int[] array;
    private int n;
    public QuickSort(int size){
        array = new int[size];
        n = 0;
    }
    public void display(){
        for(int i=0;i<n;i++){
            System.out.println(array[i]);
        }
    }
    public void insert(int value){
        array[n++] = value;
    }
    public void quickSortIt(){
        recQuickSort(0,n-1);
    }
    public void recQuickSort(int left,int right){
        if(left>=right){
            return;
        }else{
            int pivot = array[right];
            int partition = partitionIt(left,right,pivot);
            recQuickSort(left,partition-1);
            recQuickSort(partition+1,right);
        }
    }
    public int partitionIt(int left,int right,int pivot){
        int leftPtr = left - 1;//从左边-1出开始,先加1
        int rightPtr = right;//取到右边界,因为枢轴取得是最有边的值,此时array[++leftPtr]<pivot不满足,所以不会越界
        while(true){
            while(array[++leftPtr]<pivot)
                ;
            while(rightPtr>0 && array[--rightPtr]>pivot)
                ;
            if(leftPtr>=rightPtr){
                break;
            }else{
                swap(leftPtr,rightPtr);
            }
        }
        swap(leftPtr,right);
        return leftPtr;
    }
    public void swap(int left,int right){
        int temp = array[left];
        array[left] = array[right];
        array[right] = temp;
    }
}

public class QuickSortDemo {
    public static void main(String[] args) {
        int size = 10;
        QuickSort qs = new QuickSort(size);
        qs.insert(7);
        qs.insert(1);
        qs.insert(4);
        qs.insert(2);
        qs.insert(10);
        qs.insert(3);
        qs.insert(6);
        qs.insert(9);
        qs.insert(8);
        qs.insert(5);
        System.out.println("展示数据:");
        qs.display();
        System.out.println("***进行快速排序***");
        qs.quickSortIt();
        System.out.println("展示数据:");
        qs.display();
    }
}

运行结果为:

展示数据:
7
1
4
2
10
3
6
9
8
5
***进行快速排序***
展示数据:
1
2
3
4
5
6
7
8
9
10

       但是枢轴取最右边的数,可能有的时候并不那么理想,假设右边的数是最大的,那么这样数组就会被划分为两个数据规模相差极大的部分,例如数量为n,则最大的数(即枢轴)为一组,而其他n-1个数为一组,这样规模大的数组需要划分更多次(如果数组是倒序的,那么从右到左每个元素都需划分一次),理想状态是将数组通过枢轴划分为规模大小相近的两部分,这样快速排序的效率才会高。
       解决上面的弊端的方法一般是“使用三数据取中”来确定枢轴,如果数据总体规模小于等于3个,就不必要使用快速排序算法了,可以使用一般的比较方法(杀鸡焉用牛刀?)。
       三数据项取中:分别选取数组最左边、数组中间、数组最右边的数作为基础,然后排序这三个数,取中间的为枢轴,这时不仅有枢轴最终位置有序确定的好处,而且最左边的数一定比枢轴小,最右边的数一定比枢轴大,这样就不必担心跃出边界。
这里写图片描述
talk is cheap,show me the code:

public class QuickSort2 {
    private int[] array;
    private int n;
    public QuickSort2(int size){
        array = new int[size];
        n = 0;
    }
    public void display(){
        for(int i=0;i<n;i++){
            System.out.println(array[i]);
        }
    }
    public void insert(int value){
        array[n++] = value;
    }
    public void quickSortIt(){
        recQuickSort(0,n-1);
    }
    public void recQuickSort(int left,int right){
        int size = right-left+1;
        if(size<=3){
            manualSort(left,right);//只有三个元素,手动排序
        }else{
            int pivot = median(left,right);
            int partition = partitionIt(left,right,pivot);
            recQuickSort(left,partition-1);
            recQuickSort(partition+1,right);
        }
    }
    public int median(int left,int right){
        int mid = (left+right)/2;//取中间值
        if(array[left]>array[mid]){
            swap(left,mid);
        }
        if(array[mid]>array[right]){
            swap(mid,right);
        }
        if(array[left]>array[right]){
            swap(left,right);
        }
        swap(mid,right-1);//把枢轴放到右边倒数第二个,因为最右边的肯定比枢轴大
        return array[right-1];
    }
    public void manualSort(int left,int right){
        int size = right-left+1;
        if(size<=1){
            return;
        }else if(size==2){
            if(array[left]>array[right]){
                swap(left,right);
            }
            return;
        }else{
            if(array[left]>array[right-1]){
                swap(left,right-1);
            }
            if(array[left]>array[right]){
                swap(left,right);
            }
            if(array[left+1]>array[right]){
                swap(left+1,right);
            }
            return;
        }
    }
    public int partitionIt(int left,int right,int pivot){
        int leftPtr = left;//最右边的位置确定了(因为肯定比枢轴小)
        int rightPtr = right-1;//最右边的位置确定了(因为肯定比枢轴大),从枢轴开始
        while(true){
            while(array[++leftPtr]<pivot)
                ;
            while(array[--rightPtr]>pivot)
                ;
            if(leftPtr>=rightPtr){
                break;
            }else{
                swap(leftPtr,rightPtr);
            }
        }
        swap(leftPtr,right-1);
        return leftPtr;
    }
    public void swap(int left,int right){
        int temp = array[left];
        array[left] = array[right];
        array[right] = temp;
    }
}

public class QuickSortDemo2 {
    public static void main(String[] args) {
        int size = 10;
        QuickSort2 qs = new QuickSort2(size);
        qs.insert(7);
        qs.insert(1);
        qs.insert(4);
        qs.insert(2);
        qs.insert(10);
        qs.insert(3);
        qs.insert(6);
        qs.insert(9);
        qs.insert(8);
        qs.insert(5);
        System.out.println("展示数据:");
        qs.display();
        System.out.println("***进行快速排序***");
        qs.quickSortIt();
        System.out.println("展示数据:");
        qs.display();
    }
}

运行结果为:

展示数据:
7
1
4
2
10
3
6
9
8
5
***进行快速排序***
展示数据:
1
2
4
3
7
5
6
8
9
10

       快速排序的时间复杂度为O(N*logN),一般情况下,快速排序都是最快的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值