快速排序的实现及三种优化方式【Java版】

1、普通快速排序

/**
*实现快速排序(普通快速排序)
*对arr[l,,,,r]共n个元素进行排序
*用递归方法,O(nlogn)
*方法一
*缺点:在近乎有序的数组下,该快排比归并要慢很多
*因为每次排序后,左右两个子递归规模相差悬殊,构成的二叉树平衡因子没有归并好
*在完全有序时,退化为O(n^2)
*优化:随机选取基准值
*/

public class QuickSort {
   public void quickSort(int []arr,int n) {
     __quickSort(arr,0,n-1);
}
private void __quickSort(int[] arr, int l, int r) {
    //首先处理递归到底的情况,左边大于右边
    //可以优化,如果对于小数组时,可以用插入排序完成
    if(l>=r)
        return;
    int p=media(arr,l,r);

    //对arr[l,,p-1]和arr[p+1,,,r]递归,每次比基准值小的在左,大的在右
    __quickSort(arr, l, p-1);
    __quickSort(arr, p+1, r);
}
//每次以第一个元素季即arr[l]为基准,比基准值小的在右,大的在左,
//返回基准值最终在数组中的位置
private int media(int[] arr, int l, int r) {
    //
    int v=arr[l];
    int j=l;
    //int temp=arr[l];
    for(int i=l+1;i<=r;i++) {
    //每次循环将小于v的往前换
        if(arr[i]<v) {
            swap(arr,i,j+1);
            j++; //arr[j+1]始终是大于v的,arr[j]是最后一个<=v的

        }
    }
    //再将基准值移动到中间
    swap(arr,l,j);
    //最终j所指的位置就是中间值
    return j;
}
//可以用数组来作为引用
private void swap(int[] arr, int i, int j) {
    if(i!=j) {
        int temp =arr[j];
        arr[j]=arr[i];
        arr[i]=temp;
    }   
 }
}

2、优化方法一

/**
*优化:将第一个元素为基准值改为随机一个元素为基准值
*降低退化为O(n^2)的概率
*数学期望值为O(nlogn)
*这样就不会受“几乎有序的数组”的干扰了
*但是对“几乎乱序的数组”的排序性能可能会稍微下降,至少多了排序前交换的那部分
*乱序时这个交换没有意义
*/

public class QuickSort2 {
    public void quickSort2(int[]arr,int n) {
        __quickSort2(arr,0,n-1);    
    }

    private void __quickSort2(int[] arr, int l, int r) {
        if(l>=r)
            return; 
        int p=Media(arr,l,r);   
        __quickSort2(arr, l, p-1);
        __quickSort2(arr, p+1, r);  
    }

    private int Media(int[] arr, int l, int r) {
        //double类型,产生一个l到r的随机数作为数组基准值下标
        //将随机数与数组第一个数交换,即让随机数作为基准值
        //减小近乎有序的概率
        swap(arr, l, (int)Math.random()*(r-l+1)+l); 
        int v=arr[l];
        int j=l;
        for(int i=l+1;i<=r;i++) {
            if(arr[i]<v) {
                swap(arr,i,j+1);
                j++;
            }
        }
        swap(arr,l,j);  
        return j;
    }

    private void swap(int[] arr, int i, int j) {
        if(i!=j) {
            int temp =arr[j];
            arr[j]=arr[i];
            arr[i]=temp;
        }   
    }
}

3、优化方法二【两路快排】

/**
*对于方一方二中如果存在大量重复元素,当基准值为重复元素时
*等于base的这些会聚集到右侧(或者稍微改改大小关系就会聚集到左侧)。
*总之就会聚集到一边。这样在数组中重复数字很多的时候,
*就又会导致两边子递归规模差距悬殊的情况,很有可能退化为O(n^2)
*这时想把等于base的那些数分派到base两边,而不是让他们聚集到一起。
*
*优化:可以将swap去掉
*/

public class QuickSort3 {
    public void quickSort3(int[]arr,int n) {
        __quickSort3(arr,0,n-1);
    }

    private void __quickSort3(int[] arr, int l, int r) {
        if(l>=r)
            return; 
        int p=Midal(arr,l,r);       
        __quickSort3(arr, l, p-1);
        __quickSort3(arr, p+1, r);

    }

    private int Midal(int[] arr, int l, int r) {
        swap(arr,l,(int)Math.random()*(r-l+1)+l);
        //满足arr[l+1,i)<=v,arr(j,r]>=v
        int v=arr[l];
        int i=l+1,j=r;
        while(true) {
            //从左到右扫描,扫描出第一个比base大的元素,然后i停在那里
            while(arr[i]<v&&i<r)//arr[i]不能=v,会导致v聚集在一边
                i++;
            //从右到左扫描,扫描出第一个比base小的元素,然后j停在那里
            while(arr[j]>v&&j>=l)
                j--;
            if(i>=j)
                break;
            swap(arr, i, j);
            i++;
            j--;
        }
        //将基准值交换到合适位置
        swap(arr, l, j);
        return j;
    }
    private void swap(int[] arr, int i, int j) {
        if(i!=j) {
            int temp=arr[i];
            arr[i]=arr[j];
            arr[j]=temp;
        }
    }
}

4、优化方法三【三路快排】
/**
*当大量数据,且重复数多时,用三路快排
*将整个数组分为小于v,等于v,大于v三部分
*/

public class QuickSort4 {
    public void quickSort4(int[]arr,int n) {
        __quickSort4(arr,0,n-1);

    }
    private void __quickSort4(int[] arr, int l, int r) {
        if(l>=r)
            return;
    //因为==v的部分是一个数组而非单独的一个数,所以直接处理,不调用函数
        swap(arr,l,(int)Math.random()*(r-l+1)+l);   
        int v=arr[l];
        //变量定义要保证初始空间为空
        int i=l+1;//arr[lt+1,i)==v
        int lt=l;//arr[l+1,lt]<v
        int gt=r+1;//arr[gt,r]>v    
        while (i<gt) {
            if(arr[i]==v) {
                i++;
            }else if (arr[i]<v) {
                swap(arr, i, lt+1);//画示意图,arr[lt+1]==v
                i++;
                lt++;
            }else {
                swap(arr, i, gt-1);//arr[gt-1]为未处理的数据
                gt--;
            }   
        }
        swap(arr, l, lt);
        __quickSort4(arr, l, lt-1);
        __quickSort4(arr, gt, r);
    }
    private void swap(int[] arr, int i, int j) {
        if(i!=j) {
            int temp=arr[i];
            arr[i]=arr[j];
            arr[j]=temp;
        }   
    }
}

猜你喜欢

转载自blog.csdn.net/yulutian/article/details/79531909