美文网首页
入门算法:双路快速排序

入门算法:双路快速排序

作者: 半理想主义 | 来源:发表于2020-08-26 15:13 被阅读0次

    上手难度:★★★☆

    算法复杂度:O(nlogn)

    之前介绍过的快速排序,当我们整个数组中包含有大量重复键值的时候,我们的Partition操作都有可能把整个数组分成极度不平衡的两部分(例如在满足左小右大的条件后,分成的两部分右边始终占据了大量数据)
    导致快速排序退化成O(n^2)级别的算法


    image.png

    排序思想:

    换一个思路Partition的程序
    之前我们将小于v和大于v两部分全都放在了数组的一头
    然后i从左到右,逐渐遍历完整个数组
    现在将小于v和大于v两部分放在数组的两端
    分别以i作为左边的索引,j作为右边的索引
    i往右自增,j往左自减,当找到arr[i]>=v的值时,就和arr[j]<=v的值交换位置
    直至把所有小于v的值交换到【l-i】这个范围,把所有大于v的值交换到【j~r】这个范围
    直到i>j时就停止循环操作,此时i对应的就是右边第一个大于等于v的值,j对应的就是左边最后一个小于等于v的值
    再交换l和j的值,j的位置就是v值了

    代码实现:

    public class QuickSort2 {
    
        private static Random random = new Random();
    
        private static int rand(int l, int r){
            return random.nextInt(r - l + 1);
        }
    
        /**
         * 交换数组中两个元素的位置
         */
        private static void swap(int[] arr, int x, int y){
            if(x < 0 || y < 0 || x > arr.length || y > arr.length){
                return;
            }
            int temp = arr[x];
            arr[x] = arr[y];
            arr[y] = temp;
        }
    
        /**
         * 对arr[l...r]部分进行partition操作
         * 返回p,使得arr[l...p-1] < arr[p]; arr[p+1...r] > arr[p]
         */
        private static int partition2(int[] arr, int l, int r){
            swap(arr,  l, l + rand(l, r));
    
            int i = l + 1, j = r;
    
            int v = arr[l];
    
            while(true){
    
                while(i <= r && arr[i] < v){
                    //当i小于等于右边界,并且对应值小于v时,继续向右移动i,目的是把小于v的值保留在左边,同时比较下一个值
                    i++;
                }
    
                while(j >= l + 1 && arr[j] > v){
                    //当j大于等于左边界,并且对应值大于v时,继续向左移动j,目的是把大于v的值保留在右边,同时比较下一个值
                    j--;
                }
    
                if(i > j){
                    break;
                }
                swap(arr, i, j);
                //当经过了上面两层循环,此时arr[i]就是大于v的值,arr[j]就是小于v的值,所以交换
                i++;
                j--;
            }
            //上面的循环走完,i停在了右边第一个>=v的位置,j停在了左边最后一个<=v的位置
    
            swap(arr, l , j);
            //交换l和j的值,这样在j左边的都是小于等于v的,在j右边的都是大于等于v的
            return j;
        }
    
        /**
         * 对ar[l...r]部分进行快速排序
         */
        public static void quickSort(int[] arr, int l, int r){
            if(l >= r){
                return;
            }
    
            int p = partition2(arr, l, r);
            //注意:这里返回p,之后,只是说满足arr[l+1...j] < v; arr[j+1...i) > v
            //并不是说p的左边一定有数组,或者p的右边一定有数组,也可能是空
            quickSort(arr, l, p - 1);
            //以p为中间点,再进行快速排序
            quickSort(arr, p + 1, r);
        }
    
        public static void main(String[] args) {
    
            int[] arr = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
    
            quickSort(arr, 0, arr.length - 1);
    
            for( int i = 0 ; i < arr.length ; i ++ ){
                System.out.print(arr[i]);
                System.out.print(' ');
            }
    
        }
    }
    

    优点

    阻止了Quick Sort退化成O(n^2)级别的算法

    相关文章

      网友评论

          本文标题:入门算法:双路快速排序

          本文链接:https://www.haomeiwen.com/subject/ibaxsktx.html