认识二分法

经常见到的类型是在一个有序数组上,开展二分搜索但有序真的是所有问题求解时使用二分的必要条件吗?

不!!!只要能正确构建左右两侧的淘汰逻辑,你就可以二分。

例题:

1. 在一个有序数组中,找某个数是否存在

package com.zhang.chapter01;

import java.util.Arrays;

import static com.zhang.chapter01.util.RandomArrayUtil.generateRandomArray;
import static com.zhang.chapter01.util.RandomArrayUtil.generateRandomSortedArray;

/**
 * @author zhang
 * @date 2023/11/21 14:01
 * @description 二分查找 查找数组中是否存在一个数
 */
public class Code04_BSExist {

    /**
     * 二分查找
     *
     * @param sortedArr 有序数组
     * @param target    目标
     * @return true 存在 false 不存在
     */
    public static boolean binarySearch(int[] sortedArr, int target) {
        if (sortedArr == null || sortedArr.length == 0) {
            return false;
        }
        int left = 0;
        int right = sortedArr.length - 1;
        while (left <= right) {
            // 可能出现int溢出
            // int mid = (left + right) / 2;
            int mid = left + ((right - left) >> 1); // 右移运算符来代替除法运算符
            if (sortedArr[mid] == target) {
                return true;
            } else if (sortedArr[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        int maxSize = 100;
        int maxValue = 100;
        int testTime = 50000;
        for (int i = 0; i < testTime; i++) {
            int[] sortedArray = generateRandomSortedArray(maxSize, maxValue);
            int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
            boolean result = binarySearch(sortedArray, value);
            int index = Arrays.binarySearch(sortedArray, value);
            if (result != (index >= 0)) {
                System.out.println("binarySearch error!");
            }
        }
        System.out.println("binarySearch is ok!");
    }

}

2. 在一个有序数组中,找>=某个数最左侧的位置(找到第一次出现的位置)

package com.zhang.chapter01;

import java.util.Arrays;

import static com.zhang.chapter01.util.RandomArrayUtil.generateRandomSortedArray;

/**
 * @author zhang
 * @date 2023/11/21 14:23
 * @description 二分查找法: 找满足 >=target 的最左位置
 */
public class Code05_BSNearLeft {

    /**
     * 二分查找法: 找满足 >=target 的最左位置
     * @param arr 有序数组
     * @param target 目标值
     * @return 最左的元素的下标
     */
    public static int nearestIndex(int[] arr, int target) {
        if (arr == null || arr.length == 0) {
            return -1;
        }
        int left = 0;
        int right = arr.length - 1;
        // 记录最左的对号
        int index = -1;

        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            if (arr[mid] >= target) {
                index = mid;
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return index;
    }

    public static int test(int[] arr, int value) {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] >= value) {
                return i;
            }
        }
        return -1;
    }


    public static void main(String[] args) {
        int maxSize = 100;
        int maxValue = 100;
        int testTime = 50000;
        for (int i = 0; i < testTime; i++) {
            int[] sortedArray = generateRandomSortedArray(maxSize, maxValue);
            int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
            int nearestIndex = nearestIndex(sortedArray, value);
            int index = test(sortedArray, value);
            if (nearestIndex != index) {
                System.out.println("binarySearchNearLeft error!");
            }
        }
        System.out.println("binarySearchNearLeft is ok!");
    }


}

3. 在一个有序数组中,找<=某个数最右侧的位置

package com.zhang.chapter01;

import java.util.Arrays;

import static com.zhang.chapter01.util.RandomArrayUtil.generateRandomSortedArray;

/**
 * @author zhang
 * @date 2023/11/21 14:28
 * @description 二分查找: 找满足 <=target 的最右位置
 */
public class Code05_BSNearRight {

    /**
     * 二分查找: 找满足 <=target 的最右位置
     * @param arr 有序数组
     * @param target 目标值
     * @return 最右的元素的下标
     */
    public static int nearestIndex(int[] arr, int target) {
        if (arr == null || arr.length == 0) {
            return -1;
        }
        int left = 0;
        int right = arr.length - 1;
        int index = -1;

        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            if (arr[mid] <= target) {
                index = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return index;
    }

    public static int test(int[] arr, int value) {
        for (int i = arr.length - 1; i >= 0; i--) {
            if (arr[i] <= value) {
                return i;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        int maxSize = 10;
        int maxValue = 100;
        int testTime = 50000;
        for (int i = 0; i < testTime; i++) {
            int[] sortedArray = generateRandomSortedArray(maxSize, maxValue);
            int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
            int nearestIndex = nearestIndex(sortedArray, value);
            int index = test(sortedArray, value);
            if (nearestIndex != index) {
                System.out.println("sortedArray = " + Arrays.toString(sortedArray));
                System.out.println("value = " + value);
                System.out.println("binarySearchNearRight error! nearestIndex = " + nearestIndex + "  index = " + index);
                return;
            }
        }
        System.out.println("binarySearchNearRight is ok!");
    }

}

重点:局部最小值问题 (无需有序数组也可以二分)

package com.zhang.chapter01;

import java.util.Arrays;

import static com.zhang.chapter01.util.RandomArrayUtil.generateRandomArrayWithNoAdjacent;

/**
 * @author zhang
 * @date 2023/11/21 14:41
 * @description 无序数组的局部最小值问题
 * 局部最小值问题定义:
 * 数组中(无论有序还是无序 ) 任意相邻两个数不相等, 数组下标 i 的元素 小于 i - 1 下标的元素 而且 小于 i + 1 下标的元素, 就称为局部最小值.
 * 特例:
 * 1. 数组中只有一个元素, 这个元素就是 局部最小值.
 * 2. 数组0下标的元素 只要小于右边的元素, 就称为局部最小值.
 * 3. 数组最后一个元素 只要小于左边的元素, 就称为局部最小值.
 */
public class Code06_BSAwesome {
    /**
     * 局部最小值问题
     *
     * @param arr 数组
     * @return 局部最小值下标
     */
    public static int getLessIndex(int[] arr) {
        if (arr == null || arr.length == 0) {
            return -1;
        }
        if (arr.length == 1 || arr[0] < arr[1]) {
            return 0;
        } else if (arr[arr.length - 1] < arr[arr.length - 2]) {
            return arr.length - 1;
        }
        int left = 1;
        int right = arr.length - 2;
        int mid;
        while (left <= right) {
            mid = left + ((right - left) >> 1);
            if (arr[mid] > arr[mid - 1]) {
                right = mid - 1;
            } else if (arr[mid] > arr[mid + 1]) {
                left = mid + 1;
            } else {
                return mid;
            }
        }
        return -1;
    }

    /**
     * 判断一个元素是否是局部最小值
     * @param arr 数组
     * @param index 下标
     * @return 是否是局部最小值
     */
    public static boolean isRight(int[] arr, int index) {
        if (arr.length <= 1) {
            return true;
        }
        if (index == 0) {
            return arr[index] < arr[index + 1];
        }
        if (index == arr.length - 1) {
            return arr[index] < arr[index - 1];
        }
        return arr[index] < arr[index - 1] && arr[index] < arr[index + 1];
    }


    public static void main(String[] args) {
        int testTime = 500000;
        int maxSize = 50;
        int maxValue = 100;
        for (int i = 0; i < testTime; i++) {
            int[] arr = generateRandomArrayWithNoAdjacent(maxSize, maxValue);
            int ans = getLessIndex(arr);
            if (!isRight(arr, ans)) {
                System.out.println("arr = " + Arrays.toString(arr));
                System.out.println("ans = " + ans);
                System.out.println("arr[ans] = " + arr[ans]);
                System.out.println("error !");
                return;
            }
        }
        System.out.println("Test ok!");
    }

}

工具类:

package com.zhang.chapter01.util;

import java.util.Arrays;

/**
 * @author zhang
 * @date 2023/11/21 13:43
 * @description 随机数组工具类
 */
public class RandomArrayUtil {
    /**
     * 生成一个随机数组
     *
     * @param maxSize  数组最大长度
     * @param maxValue 数组最大值
     * @return 数组
     */
    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());
        }
        return arr;
    }

    /**
     * 生成一个有序的随机数组
     * @param maxSize 数组最大长度
     * @param maxValue 数组最大值
     * @return 数组
     */
    public static int[] generateRandomSortedArray(int maxSize, int maxValue) {
        int[] arr = generateRandomArray(maxSize, maxValue);
        Arrays.sort(arr);
        return arr;
    }

    /**
     * 生成一个相邻元素不相等的随机数组
     * @param maxSize 数组最大长度
     * @param maxValue 数组最大值
     * @return 数组
     */
    public static int[] generateRandomArrayWithNoAdjacent(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());
        }
        for (int i = 0; i < arr.length - 1; i++) {
            while (arr[i] == arr[i + 1]) {
                arr[i + 1] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
            }
        }
        return arr;
    }



}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值