快速排序

/*
 * 快速排序:
 * 1.分解:选取数组a中一个元素a[p]将数组a[n]分成两部分,使得左边a[0...p-1]的值都小于a[p],右边a[p+1...r]的值都大于a[p]
 * 而a[p]中的p也是由划分过程中求得的
 * 2.解决:通过递归调用快速排序,对子数组a[0..p-1]和a[p+1...r]进行排序
 * 3.合并:因为子数组都是在原址(原本数组中)是有序的了,所以不需要再进行合并排序了
 *
 * 所以快速排序关键在于划分这个方法
 * partiton():每次都将数组的(子数组)的左侧第一个元素作为枢纽,
 * 将其他元素划分好后,再将枢纽与划分结束时扫描指针(并不是C++里面的指针,只是表示指向的意思)指向元素进行交换
 * 如果是单向扫描:最后则是begin与bigger交换
 * 如果是双向扫描:最后是begin与right交换
 * 但是将左侧第一个元素作为枢纽有一定的不适用处,如果每次枢纽在划分完后交换到的位置都处于数组的某一侧,
 * 而不是数组中间一点的位置,则时间复杂度会增大
 * 所以可以用三点中值法来决定枢纽是哪个元素
 *
 * 三点中值法:比较数组最左端 中间位置 与最右端 三个元素,求出三个元素中的中值,将中值与最左端的数据项进行交换
 * 那么还是相当于将数组最左端作为枢纽,只不过这时候的枢纽是三个元素中的中值
 *
 * 在Java的类库中 Arrays.sort()方法采用的就是快速排序
 * 不过在数据项比较少的时候是采用插入排序的
 *
 * 时间复杂度是O(nlogn)
 *
 *
 *
 *
 *
 *
 */
public class QuickSort {
    public static void main(String[] args) {
        int[] arr = {54,34,2,5432,1,54,9,73,67,10};
        quickSort(arr,0,arr.length-1);
        for(int i:arr) {
            System.out.print(i+" ");
        }
    }
    
    public static void quickSort(int[] arr,int begin,int end) {
        //递归方法 也要注意边界 当begin=end时只有一个元素没必要进行排序了
        if(begin<end) {
//            int p = partition1(arr,begin,end);
            int p = partition2(arr,begin,end);
            quickSort(arr,begin,p-1);
            quickSort(arr,p+1,end);
        }
    }

    
    //划分:单向扫描 从左往右 直到扫描指针遇到右侧的bigger指针
    public static int partition1(int[] arr, int begin, int end ) {
        int privot = arr[begin];//将第一个元素设为枢纽
        int sp = begin+1;//扫描指针      begin为枢纽所以是+1
        int bigger = end;
        
        //由于交换sp与bigger指向的元素后 bigger会--,那么边界就是sp>bigger
        while(sp<=bigger) {
            if(arr[sp]<=privot)//sp指向的元素小于privot的话sp就向右移动
                sp++;
            else {//扫描遇到大于privot的元素
                swap(arr,sp,bigger);//交换扫描指针与bigger的值
                //交换了后bigger往左移
                bigger--;
                
                //这里不需要担心交换时bigger位置的元素是否大于privot
                //因为sp在这次中没有变化,
                //如果交换成sp指向的元素还是大于privot,那么下一次循环sp还是没变,
                //还是sp与bigger交换,直到bigger向左扫到比privot小的元素,交换完成后,sp才会继续向右移动
            }
        }
        swap(arr,begin,bigger);//将枢纽与bigger指向的元素交换,因为最后bigger指向的肯定是小于privot的
        return bigger;//返回bigger
    }
    
    //划分:双向扫描 两边同时扫描 遇到小于/大于就停下,交换两个指针指向的元素,然后继续 直到 left>right
    public static int partition2(int[] arr,int begin,int end) {
        //求出三个元素中的中值
        int mid = (begin+end)/2;
        if(arr[begin]<=arr[mid]&&arr[begin]>=arr[end])
            swap(arr,begin,begin);
        else if(arr[end]<=arr[mid]&&arr[end]>=arr[begin])
            swap(arr,end,begin);
        else
            swap(arr,mid,begin);
        
        int privot = arr[begin];
        int left = begin+1;
        int right = end;
        while(left<=right) {
            //注意边界问题,left不能一直扫下去,而且right右边的元素肯定是大于privot的,所以是小于等于
            //在等于的时候再加上小于或者大于privot时也是符合的 这样才能出现left>right才能跳出最外层循环
            while(left<=right&&arr[left]<=privot)//小于privot就往右移动
                left++;
            while(left<=right&&arr[right]>privot)//大于privot就往右移动
                right--;
            
            
            if(left<right) //如果是等于指向同一个没有必要交换,
                //如果left大于right就不能进行交换了,因为大于privot的要在右侧,小于的要在左侧,一交换的话就相反了
                swap(arr,left,right);
        }
        swap(arr,begin,right);//交换枢纽与right的位置
        return right;//返回最后枢纽所在的下标
    }
    
    public static void swap(int[] arr,int x,int y) {
        int temp = arr[x];
        arr[x] = arr[y];
        arr[y] = temp;
    }
    
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值