二分法-二分查找 - O(log2n)

356 阅读1分钟

1. 简述

老规矩,先看概念:baike.baidu.com/item/%E4%BA…

思路:

二分查找的前提是数组必须为有序数组

  • 第一步: 先查找数组中间位置 arr[middle],与查找的目标数字 x 是否相等;
    • 如果相等则直接返回
    • 如果 arr[middle] > x, 则代表 arr[middle] 右侧的数字都大于 x,则下次查找只需要查找 arr[middle]左边的数字
    • 如果 arr[middle] < x, 则代表 arr[middle] 左侧的数字都小于 x,则下次查找只需要查找arr[middle]右侧的数字
  • 移动左右边界,循环第一步 ...

时间复杂度:经过上面的过程分析可以知道,每次查找都可以排除一半的数字。所以时间复杂度为 O(log2n)O(log2n)

2. 实现

package com.omg.sort;

import java.util.Arrays;

/**
 * @description: 二分法 - 折半查找
 * 介绍:https://baike.baidu.com/item/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/10628618?fromtitle=%E6%8A%98%E5%8D%8A%E6%9F%A5%E6%89%BE&fromid=9796273&fr=aladdin
 */
public class Code04_BSExist {
    /**
     * @description: 查找 sortrdArr 中是否存在 num
     * @param sortedArr 有序数组
     * @param num 要查找的数据
     * @return 存在-true 不存在-false
     */
    public static boolean exist(int[] sortedArr, int num){
        if(null == sortedArr || sortedArr.length == 0){
            return false;
        }

        // 左指针
        int L = 0;
        // 右指针
        int R = sortedArr.length - 1;
        while (L < R){
            // 求 L 和 R 的中间值
            // ((R + L)/ 2) == (L + ((R - L) >> 2))
            // 因为 R, L 如果都在 Integer.MAX_VALUE 跟前,那么 L + R 容易越界
            // >> 1 与 /2 操作相同, 但是 >>1 在计算机的速度更快
            int middle = L + ((R - L) >> 1);
            if (sortedArr[middle] == num){
                return true;
            }

            if (sortedArr[middle] > num){
                R = middle - 1;
                continue;
            }else {
                L = middle + 1;
            }
        }
        
        return sortedArr[L] == num;
    }

    /**
     * @description: 对数器,用于测验上面方法的实现是否正确。
     * 对数器要求思路简单,不需要最优解。
     */
    public static boolean comparator(int[] sortedArr, int num){
        if (null == sortedArr || sortedArr.length == 0){
            return false;
        }

        boolean b = Arrays.stream(sortedArr).anyMatch(x -> x == num);
        return b;
    }

    public static int[] generateRandomArray(int maxSize, int maxValue){
        int size = (int) (Math.random() * (maxSize + 1));
        int[] res = new int[size];
        for (int i = 0; i<size; i++){
            res[i] = (int)(Math.random() * (maxValue + 1)) - (int)(Math.random() * (maxValue + 1));
        }

        return res;
    }

    public static void printArray(int[] arr){
        if (null == arr){
            return;
        }

        for (int i = 0; i< arr.length; i++){
            System.out.printf(arr[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int testTimes = 500;
        int maxSize = 100;
        int maxValue = 100;
        boolean succeed = true;
        for (int i=0; i<testTimes; i++){
            int[] arr = generateRandomArray(maxSize, maxValue);
            int num = (int)(Math.random() * (maxValue + 1)) - (int)(Math.random() * (maxValue + 1));
            // 排序,折半查找需要有序数组
            Arrays.sort(arr);
            if (exist(arr, num) != comparator(arr, num)){
                succeed = false;
                printArray(arr);
                System.out.println(num);
                System.out.println(exist(arr, num));
                System.out.println(comparator(arr, num));
                break;
            }
        }

        System.out.println(succeed ? "Nice!" : "Fucking fucked!");
    }
}