【数据结构】二分法

1. 从有序数组中找出 num

public class Code04_BSExist {

    public static boolean exist(int[] arr, int num) {
        if (arr == null || arr.length == 0) {
            return false;
        }

        int l = 0, r = arr.length - 1;
        while (l <= r) {
            // (l + r) / 2 可能会溢出
            int mid = (r - l) / 2 + l;
            if (arr[mid] == num) {
                return true;
            } else if (arr[mid] > num) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }

        return false;
    }

    /**
     * 暴力循环查找.
     */
    public static boolean test(int[] arr, int num) {
        for (int val : arr) {
            if (val == num) {
                return true;
            }
        }

        return false;
    }

    /**
     * 随机生成一个数组并排序.
     */
    public static int[] generateRandomArray(int maxSize, int maxValue) {
        int[] arr = new int[(int) ((maxSize + 1) * Math.random())];

        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
        }

        Arrays.sort(arr);
        return arr;
    }

    public static void main(String[] args) {
        int testTimes = 500000;
        int maxSize = 10;
        int maxValue = 100;
        boolean succeed = true;
        for (int i = 0; i < testTimes; i++) {
            int[] arr = generateRandomArray(maxSize, maxValue);

            int num = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
            if (test(arr, num) != exist(arr, num)) {
                succeed = false;
                break;
            }
        }

        System.out.println(succeed ? "Nice!" : "Oh no!");
    }
}

2. 有序数组中找到 >=num 最左的位置

public class Code05_BSNearLeft {

    public static int mostLeftNoLessNumIndex(int[] arr, int num) {
        if (arr == null || arr.length == 0) {
            return -1;
        }

        // 记录 ans 位置
        int res = -1;
        int l = 0, r = arr.length - 1;
        while (l <= r) {
            int mid = (r - l) / 2 + l;
            if (arr[mid] >= num) {
                res = mid;
                // 有可能 mid 的左侧还有 >=m 的值
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }

        return res;
    }

    /**
     * 暴力循环查找.
     */
    public static int test(int[] arr, int num) {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] >= num) {
                return i;
            }
        }

        return -1;
    }

    /**
     * 随机生成一个数组并排序.
     */
    public static int[] generateRandomArray(int maxSize, int maxValue) {
        int[] arr = new int[(int) ((maxSize + 1) * Math.random())];

        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
        }

        Arrays.sort(arr);
        return arr;
    }

    public static void main(String[] args) {
        int testTimes = 500000;
        int maxSize = 10;
        int maxValue = 100;
        boolean succeed = true;
        for (int i = 0; i < testTimes; i++) {
            int[] arr = generateRandomArray(maxSize, maxValue);

            int num = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
            if (test(arr, num) != mostLeftNoLessNumIndex(arr, num)) {
                succeed = false;
                break;
            }
        }

        System.out.println(succeed ? "Nice!" : "Oh no!");
    }
}

3. 有序数组中找到 >=num 最左的位置

public class Code05_BSNearRight {

    public static int mostRightNoThanNumIndex(int[] arr, int num) {
        if (arr == null || arr.length == 0) {
            return -1;
        }

        // 记录 ans 位置
        int res = -1;
        int l = 0, r = arr.length - 1;
        while (l <= r) {
            int mid = (r - l) / 2 + l;
            if (arr[mid] <= num) {
                res = mid;
                // 有可能 mid 的左侧还有 >=m 的值
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }

        return res;
    }

    /**
     * 暴力循环查找.
     */
    public static int test(int[] arr, int num) {
        for (int i = arr.length - 1; i >= 0; i--) {
            if (arr[i] <= num) {
                return i;
            }
        }

        return -1;
    }

    /**
     * 随机生成一个数组并排序.
     */
    public static int[] generateRandomArray(int maxSize, int maxValue) {
        int[] arr = new int[(int) ((maxSize + 1) * Math.random())];

        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
        }

        Arrays.sort(arr);
        return arr;
    }

    public static void main(String[] args) {
        int testTimes = 500000;
        int maxSize = 10;
        int maxValue = 100;
        boolean succeed = true;
        for (int i = 0; i < testTimes; i++) {
            int[] arr = generateRandomArray(maxSize, maxValue);

            int num = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
            if (test(arr, num) != mostRightNoThanNumIndex(arr, num)) {
                succeed = false;
                break;
            }
        }

        System.out.println(succeed ? "Nice!" : "Oh no!");
    }
}

4. 局部最小值问题

1)数组第一个元素比第二个元素小,即为局部最小值。

2)数组最后一个元素比它前一个元素小,即为局部最小值。

3)若不满足,那么局部最小值必可在数组首尾两元素之间的某个位置取得。此时可以采用二分法思想,看中间位置是否符合条件,不符合就分成两部分,从不符合的那一边继续操作。

package lyb.class1;

/**
 * @author liyibin
 * @date 2022-06-20
 */
public class Code06_BSAwesome {

    /**
     * 局部最小 无序数组,相邻的数不相等
     * 边界:
     * arr[0] > arr[1] 下降
     * arr[n-2] < arr[n - 1] 上升
     *
     * 中间 m 就是局部最小值
     * arr[m-1] > arr[m] < arr[m + 2].
     */
    public static int oneMinIndex(int[] arr) {
        if (arr == null || arr.length == 0) {
            return -1;
        }

        if (arr.length == 1) {
            return 0;
        }

        if (arr[0] < arr[1]) {
            return 0;
        }

        if (arr[arr.length - 1] < arr[arr.length - 2]) {
            return arr.length - 1;
        }

        int l = 0, r = arr.length - 1;
        // 保有三个数进行判断
        while (l < r - 1) {
            int mid = (r - l) / 2 + l;
            if (arr[mid] < arr[mid - 1] && arr[mid] < arr[mid + 1]) {
                return mid;
            } else {
                // mid - 1 > mid  mid > mid + 1
                // mid - 1 < mid  mid < mid + 1
                // mid - 1 < mid  mid > mid + 1
                if (arr[mid] > arr[mid - 1]) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            }
        }

        return arr[l] < arr[r] ? l : r;
    }

    /**
     * 生成相邻不相等的随机数组.
     */
    public static int[] generateRandomArray(int maxLen, int maxValue) {
        int[] arr = new int[(int) (Math.random() * maxLen) + 1];
        arr[0] = (int) (Math.random() * maxValue);
        for (int i = 1; i < arr.length; i++) {
            do {
                arr[i] = (int) (Math.random() * maxValue);
            } while (arr[i] == arr[i - 1]);
        }

        return arr;
    }

    /**
     * 验证得到的结果,是不是局部最小
     */
    public static boolean test(int[] arr, int index) {
        int l = index - 1;
        int r = index + 1;

        boolean left = l >= 0 ? arr[l] > arr[index] : true;
        boolean right = r < arr.length ? arr[r] > arr[index] : true;

        return left && right;
    }

    public static void main(String[] args) {
        int testTimes = 500000;
        int maxSize = 10;
        int maxValue = 100;
        boolean succeed = true;
        for (int i = 0; i < testTimes; i++) {
            int[] arr = generateRandomArray(maxSize, maxValue);

            int index = oneMinIndex(arr);
            if (!test(arr, index) ) {
                succeed = false;
                break;
            }
        }

        System.out.println(succeed ? "Nice!" : "Oh no!");
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值