荷兰国旗问题和快速排序

荷兰国旗问题有两种情况:
        1)给定一个数x,<= x的整体放在左边,>x的整体放在右边
        2)给定一个数x,< x的整体放在左边,= x的整体放在中间,> x的整体放在右边
        规定不需要辅助数组,时间复杂度O(N)完成上面的调整
        现在先来看第一个情况:

如图所示,我们先划分一个<=区域在左侧,第一个元素的下标位置是i,给定的数为x。现在我们需要对这个数组进行划分
1.当i在3这个位置的时候,i位置和<=区域的下一个位置做交换,此时就是自己跟自己交换。
2.<=区域向右移动,来到3和5之间的位置。
3,i++;
如果现在i来到6位置,发现6大于num,此时<=区域不移动,i++.
下图为最终结果

现在看第二种情况: 

如图所示,我们再次进行区域的划分,这次有了<区域和>区域
1.当当前i<num的时候,i和<区域的下一个数做交换,<区域右移,i++
2.当当前i=num的时候,直接i++
3,当当前i>num的时候,i位置和>区域的前一个数做交换,>区域左移,i不变
i不变的原因是i是新过来的,此时还没有和numb比较。
当>区域和i撞上的时候,停止。

 

 

现在看看代码:
public static void swap(int[] arr,int i,int j){
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
// arr[L...R] 玩荷兰国旗问题的划分,以arr[R]做划分值


// <arr[R] ==arr[R] > arr[R]

public static int[] netherlandsFlag(int[] arr, int L, int R) {
    if(L > R){
        return new int[]{-1,-1};    
    }
    if(L == R){
        return new int[]{L,R};    
    }
    int less = L - 1;//左边界
    int more = R;//有边界
    int index = L;//就是之前的i
    while(index < more){
        if(arr[index] == arr[R]){
            index++;        
        } else if(arr[index] < arr[R]){
            //swap(arr,less + 1,index);
            //less++;
            //index++;
            swap(arr,index++,++less);
        } else {
            swap(arr,index,--more);
        }   
    }
    //将划分值那个数和大于区域的第一个位置做交换
    swap(arr,more,R);
    //等于区域第一个数: less + 1, 最后一个more
    return new int[]{less + 1,more};
}

 

现在看快排1.0版本

1.0和荷兰国旗问题一样,只不过他是拿最后面的那个数来作为划分值,先看看这个图

 

 

第一个表示他的划分值是5,在<=区域都是<=5的,大于区域都是大于5的,现在我们拿大于区的第一个数和最后一个数做交换,就得到了第二个图形的样子。和荷兰国旗的区别就是增加了递归的过程,现在我们需要在<=5区域继续做划分,在 <=5区域的最后一个值当作划分值,然后继续划分,在>5区域的最后一个数当做划分值,继续划分。一直递归下去,总会得到一个有序的集合。
快排2.0版本

2.0版本和荷兰国旗问题的第二个情况类似,如上图所示,划分值还是最后的那个数5,在一次划分的时候出现了5的区域 

这张图的意思是在>区域的第一个数和划分值作交换,之后我们就可以得到小于5的区域在一堆,> and 5的区域做递归。总会得到有序的集合。

现在看快排3.0

 

快排1.0和2.0都有缺陷,就是最坏的时间复杂度是O(n^2),因为我们总是选数组最后一个值作为划分值,总会出现极端的情况,而且概率会比较大,那么快排3.0就是为了解决这个问题的,如上图所示,我们如果在数组中随机找一个点,然后将其对应的元素和数组最后的一个元素进行交换,那么这样的话,我们从数组中随机找到一个划分值从概率上来说会更小,我们省略了数学上的推算,这样的话他的时间复杂度达到了O(N*logN).

public static void quickSort(int[] arr){
    if(arr == null || arr.length < 2){
        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 = partition(arr,L,R);//此处数组只有两个元素,表示划分值区域的左边界和有边界
        partition(arr,L,p[0] - 1);
        partition(arr,p[1] + 1,R);    
    }
}
public static int[] partition(int[] arr,int L,int R){
    int less = L - 1;//<= 区域
    int more = R;//>区域
    int index = L;//当前数的位置
    while(index < more){
        if(arr[index] == arr[R]){
            index++;        
        } else if(arr[index] < arr[R]){
            swap(arr,index++,++less);                    
        } else {
            swap(arr,index,--more);        
        }   
    }
    swap(arr,more,R);
    return new int[]{less+1,more};
}

 在有道上写的笔记复制到这里就有点问题

我在有道上的原文链接

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值