荷兰国旗问题的解决:额外空间复杂度O(1),时间复杂度O(N)

本文介绍了荷兰国旗问题的两种变体,要求在额外空间复杂度O(1)和时间复杂度O(N)的情况下解决问题。针对第一种情况,通过leftRightPart()方法,将大于、等于和小于目标值的元素分别安排到数组的右侧、左侧和中间。对于第二种情况,使用leftMidRightPart()方法,确保小于、等于和大于目标值的元素分别位于三个不同的区域,并逐步调整。整个处理过程中,通过不断交换元素位置实现目标排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

荷兰国旗问题:Dutch National Flag Problem

        a、给定一个序列arr,和一个数字num,把 > num的放在数组右边,把 <= num的放在数组左边
        b、给定一个序列arr,和一个数字num,把>num的放在数组右边,把 < num的放在数组左边,等于的放在数组中间
        a,b题目的要求:额外空间复杂度O(1),时间复杂度O(N) 

思路:

        题目a思路:对应leftRightPart()方法

        当arr[k] <= num:假定有一个<=的区域(用left  =  -1代表 <= num的初始区域),k指向当前的值(k从0开始),如果arr[k] <= num,交换当前指向的值与 <= 区域下一位的值,left ++(让<=这个区域往数组右边推移),让k ++,交换的目的是为了让小的值纳入到<=的区域内,这个区域内的经过上述操作后就都是 <= num的值。

        当arr[k] > num:这时候直接k ++,直到k走到数组越界为止,整个过程即可完成,k越过的都是比num大的值,k 和 left + 1位置(<=区域下一位)交换的值都是将小的值纳入 <= 区域的过程。

        题目b思路:对应leftMidRightPart()方法

        当arr[k] < num:假定有两个区域(用left = -1代表小于num的初始区域,用right = arr.length代表大于num的初始区域),k指向当前的值(k从0开始),如果arr[k]  <  num,arr[k] 和 arr[left+1]的值做交换,然后left ++k ++

        当arr[k] > num:如果arr[k]  >  num,交换arr[k] 和 arr[arr.length-1]的值,然后right --,right的这个处理是为了将刚才调整到的位置上的值纳入到   > num 的区域内 。   这里arr[arr.length-1]    是 > num 区域的前一位的值,那么交换完,后面的值换到了k的位置,这时候k位置的值不确定它与num的关系,所以让循环的索引 i -= 1,这一步的目的是将已经执行过的一次循环往回调了一下,让换过来的在k位置上的值重新进入判断逻辑。

        当arr[k] = num:直接跳过,然后k ++

        当k == right时,return,处理完成。

小结:其实b思路就好像在一个桌子上放着一串带有数组的棋子,给定一个规则:确定一个数字,大于这个数字的棋子我拿,小于这个数字的棋子你拿,和这个数字相等的谁也别动。你和我分别代表< num的区域和 > num的区域,这两个区域外侧的都是待处理的数字,待处理区域初始为整个数组。a思路就是b思路的简化.....

package com.kali.quick;

import java.util.Arrays;

/**
 * 荷兰国旗问题:Dutch National Flag Problem
 *      1.给定一个序列arr,和一个数字num,把 > num的放在数组右边
 *      把 <= num的放在数组左边
 *      2.给定一个序列arr,和一个数字num,把>num的放在数组右边
 *      把 < num的放在数组左边,等于的放在数组中间
 *      1,2的要求:额外空间复杂度O(1),时间复杂度O(N)
 */
public class NetherlandsFlagIssue {
    public static void main(String[] args) {
        int[] arr = {1,3,4,2,7,5,11,2,6,9,8,15,7,7,9,7,7,6,4};
        /*int[] process = process(arr, 3);
        System.out.println(Arrays.toString(process));*/
        /*int[] ints = leftRightPart(arr, 4);
        System.out.println(Arrays.toString(ints));*/
        int[] tar = leftMidRightPart(arr, 7);
        System.out.println(Arrays.toString(tar));
    }

    //<= num在左边,> num在右边
    public static int[] leftRightPart(int[] arr,int num){
        int left = -1;
        int k = 0;
        for (int value : arr) {
            if (value <= num) {
                swap(arr, k, left + 1);
                left++;
            }
            k++;
        }
        return arr;
    }

    //真正意义上的荷兰旗问题 < num在左边,> num在右边 = num在中间
    public static int[] leftMidRightPart(int[] arr,int num){
        int left = -1;
        int right = arr.length;
        int k = 0;
        for (int i = 0; i < arr.length; i++) {
            if(k == right){
                break;
            }
            if (arr[i] < num) {
                swap(arr, k, left+1);
                left++;
                k ++;
            }else if(arr[i] > num){
                swap(arr,k,right-1);
                i -= 1;
                right --;
            }else{
                k ++;
            }
        }
        return arr;
    }

    public static void swap(int[] arr,int m,int n){
        int temp = arr[m];
        arr[m] = arr[n];
        arr[n] = temp;
    }

    //按自己想法写的,只实现了<= num在左边,> num在右边,可能不满足复杂度要求
    public static int[] process(int[] arr,int wall){
        int[] des = new int[arr.length];
        int L = 0;
        for (int item : arr) {
            if (item <= wall) {
                des[L] = item;
                L++;
            }
        }

        for (int value : arr) {
            if (value > wall) {
                des[L++] = value;
            }
        }
        return des;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值