算法中二分搜索详解

文章目录

在有序数组中找num是否存在

实现思路

  1. 先定义左索引为0,有索引为数组长度-1
  2. 然后定义中间索引(在左索引和右索引的中间)
  3. 然后拿num和中间索引对应的数作比较,相等的话代表这个数存在。
  4. 如果num大于中间索引的数,因为是有序数组,中间索引左边的数都小于等于中间索引的数,所以要查找的数的范围是在中间索引右边到右索引里面,让其左索引等于中间索引加1,然后再从步骤2开始
  5. 如果要查找的数小于中间索引的数,那么应该在左索引到中间索引这个范围内,让右索引等于中间索引减1,再从步骤2开始

实现代码(里面运用了对数器)

java 复制代码
  public static void main(String[] args) {

        int N = 100;
        int V = 1000;
        int testTime = 500000;
        System.out.println("测试开始");
        for (int i = 0; i < testTime; i++) {
            //[0,1) * 100  <=> [0,100)
            int n = (int) (Math.random() * N);
            int[] arr = randomArray(n, V);
            Arrays.sort(arr);
            int num = (int) (Math.random() * V);
            int a = right(arr,num);
            int b = getIndex(arr,num);
            if (a != b){
                System.out.println("出错了!");
            }
        }

        System.out.println("测试结束");

    }

    public static int right(int[] arr, int num) {
        if (Arrays.binarySearch(arr, num) >= 0){
            return Arrays.binarySearch(arr, num) ;
        }else {
            return -1;
        }
    }

    public static int getIndex(int[] arr, int num) {
        if (arr == null || arr.length == 0) {
            return -1;
        }
        int left = 0;
        int right = arr.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (arr[mid] == num) {
                return mid;
            } else if (arr[mid] > num) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return -1;
    }

    public static int[] randomArray(int n, int v) {
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            //[0,1) * 1000 + 1 <=> [1,1000]
            arr[i] = (int) (Math.random() * v) + 1;

        }
        return arr;
    }

在有序数组中找>=num的最左位置

实现思路

  1. 先定义左索引为0,有索引为数组长度-1
  2. 然后定义中间索引(在左索引和右索引的中间)
  3. 定义最终一个索引为-1
  4. 判断中间索引的元素和num的比较情况
    1. num大,所以在中间索引右边的区域,重复步骤2
    2. num小,在左边的区域,让最终索引等于中间索引,看左边区域,重复步骤2
  5. 返回最终索引

代码实现

java 复制代码
 public static void main(String[] args) {
        int N = 100;
        int V = 1000;
        int testTime = 500000;
        System.out.println("测试开始");
        for (int i = 0; i < testTime; i++) {
            int n = (int) (Math.random() * N);
            int[] arr = randomArray(n, V);
            Arrays.sort(arr);
            int num = (int) Math.random() * V;
            if (rightIndex(arr,num) != getIndex(arr,num)){
                System.out.println("出错了!");
            }
        }
        System.out.println("测试结束");
    }

    public static int[] randomArray(int n, int v) {
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = (int) (Math.random() * v + 1);
        }

        return arr;
    }

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

    public static int getIndex(int[] arr, int num) {
        int left = 0;
        int right = arr.length - 1;
        int ans = -1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (arr[mid] < num){
                left = mid + 1;
            }else {
                ans = mid;
                right = mid -1;
            }
        }

        return ans;
    }

在有序数组中找<=num的最右位置

实现思路

  1. 和上面第二个的思路差不多。
  2. 用暴力解求索引时让其从后往前遍历,然后让其元素小于等于num
  3. 在通过最优解(二分查找)时,中间索引大于num时,范围就编程中间索引的左边;反之最终索引等于中间索引,然后范围在中间索引的右边

实现代码

java 复制代码
public static void main(String[] args) {
        int N = 100;
        int V = 1000;
        int testTime = 500000;
        System.out.println("测试开始");
        for (int i = 0; i < testTime; i++) {
            int n = (int) (Math.random() * N);
            int[] arr = randomArray(n, V);
            Arrays.sort(arr);
            int num = (int) Math.random() * V;
            if (rightIndex(arr,num) != getIndex(arr,num)){
                System.out.println("出错了!");
            }
        }
        System.out.println("测试结束");
    }

    public static int[] randomArray(int n, int v) {
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = (int) (Math.random() * v + 1);
        }

        return arr;
    }

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

    public static int getIndex(int[] arr, int num) {
        int left = 0;
        int right = arr.length - 1;
        int ans = -1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (arr[mid] > num){
                right = mid - 1;
            }else {
                ans = mid;
                left = mid + 1;
            }
        }

        return ans;
    }

二分搜索不一定发生在有序数组上(比如寻找峰值问题)

题目描述

峰值元素是指其值严格大于左右相邻值的元素。

给你一个整数数组 arr,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。

你可以假设 arr[-1] = arr[n] = -∞ 。

你必须实现时间复杂度为 O(log n) 的算法来解决此问题。

实现思路

  1. 先判断数组个数
    1. 如果个数为1,那么这个数就是峰值,返回0索引
    2. 如果不为1,也就是大于等于2
  2. 数组长度大于等于2
    1. 判断第一个数和第二个数的大小,如果第一个数大于第二个数,那么第一个数就是峰值
    2. 判断倒数第一个数和倒数第二个数,如果倒数第一个数大于倒数第二个数,那么倒数第一个数就是峰值
  3. 不属于步骤2的俩个条件,那么就可以用二分搜索
    1. 定义中间索引
    2. 判断中间索引数和中间索引数的前一个数,如果中间索引数小于中间索引数的前一个数,那么中间索引左边的范围必有峰值
    3. 判断中间索引数和中间索引数的后一个数,如果中间索引数小于中间索引数的后一个数,那么中间索引右边的范围必有峰值
    4. 如果上述bc都不成立,那么中间索引数为峰值

实现代码

java 复制代码
public int findPeakPoint(int[] arr) {
        int n = arr.length;
        if (arr == null || arr.length == 0) {
            return -1;
        }
        if (arr.length == 1) {
            return 0;
        }

        if (arr[0] > arr[1]) {
            return 0;
        }
        if (arr[n - 1] > arr[n - 2]) {
            return n - 1;
        }
        
        int left = 1;
        int right = n - 2;
        int ans = -1;

        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (arr[mid - 1] > arr[mid]) {
                right = mid - 1;
            } else if (arr[mid] < arr[mid + 1]) {
                left = mid + 1;
            } else {
                ans = mid;
                break;
            }
        }

        return ans;
    }
相关推荐
魔道不误砍柴功2 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2342 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
pianmian12 小时前
python数据结构基础(7)
数据结构·算法
闲晨2 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
测开小菜鸟3 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
好奇龙猫4 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
P.H. Infinity4 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天4 小时前
java的threadlocal为何内存泄漏
java
sp_fyf_20244 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
caridle4 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express