美文网首页
数组的parition过程以及快速排序的分析

数组的parition过程以及快速排序的分析

作者: chengcongyue | 来源:发表于2019-04-09 22:41 被阅读0次

    1,数组的parition过程

    parition(分割)

    例题1

    调整有序数组arr,调整arr使得这个数组的左半部分有序且不重复,但右不一定有序

    //两个变量,cur一直向前移动,pre一直被比较
    public static int partition1(int[] arr)
        {
            int pre=0;
            int cur=0;
            while(cur!=arr.length)
            {
                if(arr[cur]!=arr[pre])
                {
                    swap(arr,++pre,cur++);
                }
                else
                {
                    cur++;
                }
            }
            System.out.println(Arrays.toString(arr));
            return 0;
        }
    

    优化之后的代码

    public static void partition2(int[] arr)
        {
            if(arr==null||arr.length<2)
            {
                return ;
            }
            int u=0;//pre
            int i=1;//cur
            while(i!=arr.length)
            {
                if(arr[i++]!=arr[i])
                {
                    swap(arr,i-1,u++);
                }
            }
        }
    

    例题2

    荷兰国旗问题

    public static int partition(int[] arr,int pivot)
        {
            int less=-1;
            int more=arr.length;
            int cur=0;
            while(cur<more)
            {
                if(arr[cur]>pivot)
                {
                    swap(arr,cur,--more);
                }else if(arr[cur]<pivot)
                {
                    swap(arr,cur++,++less);
                }else 
                {
                    cur++;
                }
            }
            System.out.println(Arrays.toString(arr));
            System.out.println(cur+" "+more+" "+less);
            return cur;
        }
    

    注意开始时的三个变量,cur,less,more,其含义为
    cur表示一直在移动的下标,less表示最小区,开始时,没有一个元素在最小区,所以为-1,more和less恰好相反,表示最大区开始值为arr.length
    我们来看一下运行的结果:


    运行结果

    我们看到less区为0到7,more区为9到最后,而且我们发现cur等于more,这是恰好是循环跳出的条件
    我们在回过头来看一下核心逻辑

     while(cur<more)
            {
                if(arr[cur]>pivot)//如果当前的值大于比较的值
                {
                    swap(arr,cur,--more);//将more区的前一个元素和当前的元素进行交换
                }else if(arr[cur]<pivot)
                {
                    swap(arr,cur++,++less);//将less区的前一个元素和当前的元素进行交换
                }else 
                {
                    cur++;
                }
            }
    

    这里有一个问题,为什么和++less位置交换时,可以cur++,而和--more区域的交换时,cur不会++


    cur++的原因

    2,修改parition方法的参数

    在上面使用的时候开始的区域默认是0,结束的区域默认是arr.length-1,这时候我们将开始的下标和结束的下标都改成不确定的值,然后将返回值设置为一个数组,其含义是arr[0]表示less的区域,arr[1]表示more的区域

    public static int[] parition(int[] arr,int l,int r,int pivot)
        {
            int less=l-1;
            int more=r+1;
            int cur=0;
            while(cur<more)
            {
                if(arr[cur]>pivot)
                {
                    swap(arr,cur,--more);
                }else if(arr[cur]<pivot)
                {
                    swap(arr,cur++,++less);
                }else 
                {
                    cur++;
                }
            }
            return new int[]{less+1,more-1};
        }
    

    这时候我们就开始学习快速排序了

    3,快速排序

    public static void quickSort(int[] arr)
        {
            if(arr==null||arr.length==0)
            {
                return ;
            }
            quickSort(arr,0,arr.length-1);
        }
        
        public static void quickSort(int[] arr,int l,int r)
        {
            if(l<r)
            {
                int[] p=parition(arr, l, r, arr[r]);
                quickSort(arr,l,p[0]-1);
                quickSort(arr,p[1]+1,r);
            }
        }
        
        public static int[] parition(int[] arr,int l,int r,int pivot)
        {
            int less=l-1;
            int more=r+1;
            int cur=l;
            while(cur<more)
            {
                if(arr[cur]<pivot)
                {
                    swap(arr,++less,cur++); 
                }
                else if(arr[cur]>pivot)
                {
                    swap(arr,--more,cur);   
                }
                else
                {
                    cur++;
                }
            }
            return new int[]{less+1,more-1};
        }
    

    快速排序需要注意的是,边界问题,less,more什么时候加1,什么时候减1
    parition返回的是less区域的后一个值,more区域的前一个值,所以在递归调用的时候p[0]-1,p[1]+1.
    然后我们注意到,每一个的parition的过程我们都是使用arr[r]作为划分值的,即当前子数组的最后一个元素.

    4,快速排序的优化以及代码的简化

    当快速排序的数组本身是倒叙的,那么我们每次都取子数组的最后一个元素,这时候就是O(n^2)的时间复杂度,为了简化它,我们可以在数组中选择一个随机的元素作为划分值
    从减少代码量的角度我们可以减少一个变量,pivot,即每一次都默认使用最后一个元素,而不用传入

    public static void quickSort(int[] arr)
        {
            if(arr==null||arr.length==0)
            {
                return ;
            }
            quickSort(arr,0,arr.length-1);
        }
        
        public static void quickSort(int[] arr,int l,int r)
        {
            if(l<r)
            {
                swap(arr,l+(int)(Math.random()*(r-l+1)),r);//我们将数组中随意一个元素和结尾的元素交换
                int[] p=parition(arr, l, r);
                quickSort(arr,l,p[0]-1);
                quickSort(arr,p[1]+1,r);
            }
        }
        
        public static int[] parition(int[] arr,int l,int r)
        {
            int less=l-1;
            int more=r;//arr[r]作为划分的元素
            int cur=l;
            while(cur<more)
            {
                if(arr[cur]<arr[r])
                {
                    swap(arr,++less,cur++); 
                }
                else if(arr[cur]>arr[r])
                {
                    swap(arr,--more,cur);   
                }
                else
                {
                    cur++;
                }
            }
            swap(arr,r,more);//最后交换即可
            return new int[]{less+1,more};//注意到这是的边界条件
        }
        
    

    5,快速排序的分析

    快速排序的分析

    快速排序的空间复杂度来自于递归要保留现场
    最优的情况下空间复杂度为:O(logn) ;每一次都平分数组的情况
    最差的情况下空间复杂度为:O( n ) ;退化为冒泡排序的情况

    相关文章

      网友评论

          本文标题:数组的parition过程以及快速排序的分析

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