算法-随机快排

7 篇文章 1 订阅

目录

一、引:荷兰国旗问题

二、快排1.0:每次只排一个数

三、快排2.0:每次排一批数

四、快排3.0:随机快排

五、时间复杂度

1、快排1.0时间复杂度

2、快排2.0时间复杂度

3、快排3.0时间复杂度


一、引:荷兰国旗问题

一个数组[5,3,7,2,3,4,8,1],给一个数num=4,需要将数组左边放小于4的,中间放等于4的,右边放大于4的。

步骤:

1、设置一个小于区,大于区

2、把数组值一个一个和num比较,

第一个是5,当arr[i] > num时,arr[i]和大于区的前一个互换位置,大于区扩大一个

指针在第一个是1,当arr[i] < num时,1和小于区的下一个(即自己)换位置,小于区扩大一个单位,指针跳下一个

下一个是3,当arr[i] < num时,3和小于区的下一个(即自己)换位置,小于区扩大一个单位,指针跳下一个

下一个是7,当arr[i] > num时,arr[i]和大于区的前一个互换位置,大于区扩大一个

指针在8,当arr[i] > num时,arr[i]和大于区的前一个互换位置,大于区扩大一个

指针在3,当arr[i] = num时,指针直接跳下一个

下一个是2,当arr[i] < num时,2和小于区的下一个换位置,小于区扩大一个单位,指针跳下一个

下一个是3,当arr[i] < num时,3和小于区的下一个换位置,小于区扩大一个单位,指针跳下一个

下一个就到了大于区的第一个,指针停

就排好了。

代码: 

小于区、大于区,不是真的设置两个数组,只是两个指针的移动,最后我们可以返回一个数组,表示等于区的第一个数和最后一个数位置。

这里num是数组的最后一个数

public static int[] randomQuickSort(int[] arr,int l,int r){
		if(l==r){
			return new int[]{l,r};
		}
		if(l>r){
			return new int[]{-1,-1};
		}
		int less = l-1;
		int more = r;
		int num = arr[r];
		while(l<more){
			if(arr[l] < num){
				swap(arr,l++,++less);
			}else if(arr[l]>num){
				swap(arr,l,--more);
			}else{
				l++;
			}
		}
		//r位置上的数要和大于区第一个(l停留的位置)呼唤
		swap(arr,more,r);
		return new int[]{++less,l};
	}
public static void swap(int[] arr,int a,int b){
		int temp = arr[a];
		arr[a] = arr[b];
		arr[b] = temp;
	}

二、快排1.0:每次只排一个数

每次以数组最后一个数比如X做基准,数组前面部分是<=X,数组后面是>X的,最后把X放到>X区的第一位,那么这个X就排好序了。

下一步,排<=X区,排好最右侧的数,

再下一步,排新<=X区...

这就是一个递归,其中递归的return 一定是左指针=右指针的时候,说明这个数就排好了(左右指针重逢,就一个数),而左右指针的确定,左部分的左指针永远是0,左部分的右指针是X-1位置的;右部分的左指针的是X+1位置,右部分的右指针是整个数组最后的位置

而怎么求X的位置,需要写一个方法实现,这个方法传入数组,以arr[r]为X,求出X位置,返回一个数组,X-1位置和X+1位置(荷兰国旗问题)。

递归的目的是不断缩小l-r的范围,求这个范围的排序,那么【求这个范围的排序】这个是另写的方法,这个方法返回等于X的位置,作为下一个l-r的范围。

public void randomQuick(int[] arr,int l,int r){
        if(l>=r){
            return;
        }
        int newPosition = process(arr,l,r);
        randomQuick(arr,l,newPosition-1);
        randomQuick(arr,newPosition+1,r);
    }

【求这个范围的排序】是 process(arr,l,r) 方法

public int process(int[] arr,int l,int r){
        if(l==r){
            return l;
        }
        if(l>r){
            return -1;
        }
        int num = arr[r];
        int less = l-1;
        int more = r;
        while(l<r){
            if(arr[l]>num){
                l++;
            }else{
                swap(arr,l++,++less);
            }
        }
        swap(arr,more,less+1);
        return less+1;
    }

三、快排2.0:每次排一批数

比如数组[5,3,6,4,1,3],以最后一个数为基准,排序成<num的在左边,等于num的在中间,大于num的在右边,返回num区的前后位置,做下一次的排序边界。

那么递归目的就是不断缩小l-r的范围,做这个范围的排序,【求这个范围的排序】这个是另写的方法,这个方法返回的即等于区的前后位置,前位置-1是下一个左部分的r,后位置+1是下一个右部分的l。

//快排2.0
    public void randomQuick2(int[] arr,int l,int r){
        if(l>=r){
            return;
        }
        int[] equalsZone = process2(arr,l,r);
        randomQuick2(arr,l,equalsZone[0]-1);
        randomQuick2(arr,equalsZone[1]+1,r);
    }

    public int[] process2(int[] arr,int l,int r){
        if(l==r){
            return new int[]{l,l};
        }
        if(l>r){
            return new int[]{-1,-1};
        }
        int num = arr[r];
        int less = l-1;
        int more = r;
        while(l<more){
            if(arr[l] > num){
                swap(arr,l,--more);
            }else if(arr[l] == num){
                l++;
            }else{
                swap(arr,l++,++less);
            }
        }
        swap(arr,more,r);
        return new int[]{less+1,l};
    }

四、快排3.0:随机快排

每次进行最后一个数做num之前,先把最后一个数和数组中某个位置的数互换,然后进行荷兰国旗。

比如[5,3,6,4,1,3],先把最后一个数3和数组随机一个数互换,比如和6互换了,然后数组为[5,3,3,4,1,6],然后6做为num,进行荷兰国旗排序。下一次的时候继续把最后一个数和数组随机一个数互换。

public void randomQuick3(int[] arr,int l,int r){
        if(l>=r){
            return;
        }
        swap(arr, l+(int)(Math.random()*(r-l+1)),r);
        int[] equalsZone = process3(arr,l,r);
        randomQuick3(arr,l,equalsZone[0]-1);
        randomQuick3(arr,equalsZone[1]+1,r);
    }

    public int[] process3(int[] arr,int l,int r){
        if(l==r){
            return new int[]{l,l};
        }
        if(l>r){
            return new int[]{-1,-1};
        }
        int num = arr[r];
        int less = l-1;
        int more = r;
        while(l<more){
            if(arr[l] > num){
                swap(arr,l,--more);
            }else if(arr[l] == num){
                l++;
            }else{
                swap(arr,l++,++less);
            }
        }
        swap(arr,more,r);
        return new int[]{less+1,l};
    }

五、时间复杂度

1、快排1.0时间复杂度

以差情况看时间复杂度,比如数据[1,2,3,4,5,6,7],第一次,以7为num,做快排,结果只完成了7这个位置的数,时间复杂度O(N);第二次以6做num,也只完成了6这个位置的数,时间复杂度O(N)....一共进行了N次,所以时间复杂度是O(N^2)

2、快排2.0时间复杂度

最差情况[1,2,3,4,5,6,7],同1一样,时间复杂度是O(N^2)

3、快排3.0时间复杂度

最差情况[1,2,3,4,5,6,7],经过random,到了最好情况[1,2,3,7,5,6,4],左右规模是2/N,时间复杂度是T(N) = T(N/2) +  T(N/2)  + O(N),即O(N*log^N)

遇到最差情况时,[1,2,3,4,5,6,7] ,时间复杂度O(N^2)

当N很大时,我们每次都随机选一个数,那么就是每次随机之后,好情况和差情况的时间复杂度趋向的值,就是最终时间复杂度。

数学证明,O(N^2) 和 O(N*log^N) 最终趋向 O(N*log^N)。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值