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

2019-04-09  本文已影响0人  chengcongyue

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 ) ;退化为冒泡排序的情况

上一篇 下一篇

猜你喜欢

热点阅读