荷兰国旗问题 、快排

引例:

        给定一个数组nums,和一个数n,请把小于等于n的数放在数组的左边,大于n的数放在数组的右边。要求额外空间复杂度O(1),时间复杂度 O(N)

                只是要求把数组分成两块,小于等于区域和大于区域

实现如下:

 上述过程可分为两种情况:

  • 当nums[i] <= n时,将nums[i]与小于等于区域的下一个数进行交换,小于等于区域向右扩展一位,并且i++
  • 当nums[i] > n时,i++

        直到i越界结束,可以理解为  小于等于区域  向右扩展推挤  大于区域

代码:

public static void partition(int[] nums, int n ){
        int less = -1;//小于区域右边界
        int i = 0;
        while(i < nums.length){
            if(nums[i] <= n){
                swap(nums,++less,i++);
            }else{
                i++;
            }
        }
    }

    public static void swap(int[] nums,int i,int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

荷兰国旗问题实现类似:

        问题:给定一个数组nums,和一个数n,请把小于n的数放在数组的左边,等于n的数放在数组的中间,大于n的数放在数组的右边。要求额外空间复杂度O(1),时间复杂度O(N)

        荷兰国旗问题与上述问题的区别就是要把nums分为  小于区域、等于区域、大于区域   三部分,

类比上述问题,可以让小于区域向右推挤,再让大于区域向左推挤,这样中间自自然就是等于区域了。

过程如下:

  • nums[i] < n,将小于区域的下一个数与num[i]交换,小于区域右扩,i++;
  • num[i] == n,i++;
  • nums[i] > n,将大于区域的前一个数与nums[i]交换,大于区域左扩,i不变,这里i不变的原因是nums[i]是由右边区域的数交换过来的,并没有进行比较与n,所以i不能右移。

代码如下:

public void partition(int[] nums,int n){
    int less = -1;//初始小于区域右边界
    int more = nums.length;//大于区域初始左边界
    int i = 0;
    while(i < more){
        if(nums[i] < n){
            swap(nums,++less,i++);
        }else if(nums[i] > n){
            swap(nums,--more,i);
        }else{
            i++;
        }
    }
}
public static void swap(int[] nums,int i,int j){
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

由荷兰国旗问题引出快排的实现

        荷兰国旗问题可以将数组nums中的某个重复出现的数放在一起(等于区域),其实在经过荷兰国旗问题将数组划分为三个区域后,中间的(等于区域)的位置就是排序后的位置,因为它左边全是小于它的数,右边全是大于它的数。

        可以理解为经过一次的partition(荷兰国旗问题处理)我们就确定了一批数(这个数出现了多次)/ 一个数(这个数只出现了一次)的位置,那么我们把partition后的  小于区域  和  大于区域  送入递归,让他们继续进行partition,每次partition都确定一批数 / 一个数,这样当小于区域和大于区域只有一个数时,就已经完成了排序。这也就是快排的过程。

        只不过我们要对partition的返回值加以修改,让它返回 等于区域 的左右区间,用于递归时来分割递归区域。

        代码实现:

    public static void quickSort(int[] nums,int L,int R){
      if(L < R) {
          
          int[] p = partition(nums, L, R);
          quickSort(nums, L, p[0] - 1);
          quickSort(nums, p[1] + 1, R);
      }
    }
    public static int[] partition(int[] nums,int L,int R){
        int less = L - 1;
        int more = R + 1;
        int i = L;
        while(i < more){
            if(nums[i] < nums[R]){
                swap(nums,++less,i++);
            }else if(nums[i] > nums[R]){
                swap(nums,--more,i);
            }else{
                i++;
            }
    }
        //返回等于区域的左右区间
        int[] amount = {less + 1,more - 1};
        return amount;
    }

 public static void swap(int[] nums, int i ,int j) {
        nums[i] = nums[i] ^ nums[j];
        nums[j] = nums[i] ^ nums[j];
        nums[i] = nums[i] ^ nums[j];
    }

        可是上述快排的时间复杂度是O(N^2),导致N^2的原因是我们每次都默认nums的最后一个数作为划分,这样我们可以人为 的使数组进行两次递归时的权重严重失衡,使得成为了N^2的算法,我们只要使每次的划分值的选取是一个随机事件,最终的时间复杂度就降到了O(NlogN),(具体证明过于复杂,这里不做证明,给出传送门>>>快排时间复杂度的证明)下面是最终的完整代码:

    public static void quickSort(int[] nums,int L,int R){
      if(L < R) {
           //随机选取划分值放到数组末尾
          swap(nums,L + (int)Math.random() * (R - L + 1),R);
          int[] p = partition(nums, L, R);
          quickSort(nums, L, p[0] - 1);
          quickSort(nums, p[1] + 1, R);
      }
    }
    public static int[] partition(int[] nums,int L,int R){
        int less = L - 1;
        int more = R + 1;
        int i = L;
        while(i < more){
            if(nums[i] < nums[R]){
                swap(nums,++less,i++);
            }else if(nums[i] > nums[R]){
                swap(nums,--more,i);
            }else{
                i++;
            }
    }
        int[] amount = {less + 1,more - 1};
        return amount;
    }

  public static void swap(int[] nums, int i ,int j) {
        nums[i] = nums[i] ^ nums[j];
        nums[j] = nums[i] ^ nums[j];
        nums[i] = nums[i] ^ nums[j];
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
荷兰国旗问题是一个经典的排序问题,它要求按照荷兰国旗的颜色顺序(红、白、蓝)对一个包含红、白、蓝三种颜色的数组进行排序。 快速排序算法可以用来解决荷兰国旗问题。该算法基于分治的思想,通过多次划分和交换元素来达到排序的目的。以下是快速排序算法解决荷兰国旗问题的步骤: 1. 选择一个枢轴元素(一般是数组的最后一个元素)。 2. 初始化三个指针:low指向数组的起始位置,high指向数组的末尾位置,mid指向数组的起始位置。 3. 从头遍历数组,如果当前元素小于枢轴元素,则交换当前元素和mid指针指向的元素,并将mid指针后移一位。 4. 如果当前元素等于枢轴元素,则将high指针前移一位。 5. 如果当前元素大于枢轴元素,则交换当前元素和high指针指向的元素,并将high指针前移一位。 6. 重复步骤3到步骤5,直到low指针和high指针相遇为止。 7. 最后,将枢轴元素放在mid指针的位置上,这样数组就被分成了三个部分:小于枢轴元素的部分、等于枢轴元素的部分和大于枢轴元素的部分。 8. 递归地对小于和大于枢轴元素的部分进行排序。 以下是一个示例荷兰国旗问题的快速排序的实现(使用Python语言): ```python def dutch_flag_sort(arr): def swap(arr, i, j): arr[i], arr[j] = arr[j], arr[i] def quicksort(arr, low, high): if low < high: pivot = arr[high] mid = low for i in range(low, high): if arr[i] < pivot: swap(arr, i, mid) mid += 1 swap(arr, mid, high) quicksort(arr, low, mid - 1) quicksort(arr, mid + 1, high) quicksort(arr, 0, len(arr) - 1) return arr # 示例用法 arr = [2, 0, 2, 1, 1, 0] sorted_arr = dutch_flag_sort(arr) print(sorted_arr) ``` 上述示例代码会输出 `[0, 0, 1, 1, 2, 2]`,即按照荷兰国旗顺序排序的结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值