Home 有序数组查找数
Post
Cancel

有序数组查找数

总目标

  1. 在有序数组中判断元素是否存在 —— 标准二分查找
  2. 在有序数组中找>=num的最左位置
  3. 在有序数组中找<=num的最右位置
  4. 二分搜索的扩展 —— 不一定要求数组有序,比如寻找峰值
  5. 二分答案法 —— 一类重要的算法思想(如最小化最大值、最大化最小值问题)

判断有序数组中是否存在目标数

思路

  • 典型二分查找,时间复杂度 O(log n)

  • 每次取中点 m,比较 arr[m] 与 num:

    • 相等 → 返回 true

    • 中点值大 → 右边界左移

    • 中点值小 → 左边界右移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static boolean exist(int[] arr, int num) {
        if (arr == null || arr.length < 2) {
            return false;
        }
        int l = 0, r = arr.length - 1, m = 0;
        while (l <= r) {
            m = (l + r) / 2;
            if (arr[m] == num) {
                return true;
            } else if (arr[m] > num) {
                r = m - 1;
            } else if (arr[m] < num) {
                l = m + 1;
            }
        }
        return false;
    }

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

思路

  • 找到满足条件的第一个位置

  • 核心:一旦 arr[m] >= num,记录答案并继续向左二分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 public static int findLeft(int[] arr, int num) {
        int l = 0, r = arr.length - 1, m = 0;
        int ans = -1;
        while (l <= r) {
//            m = (l+r)/2;
//            m = l + (r - l) / 2; // 防溢出
            //非负数向右移动一位等于除以2
            m = l + ((r - 1) >> 1);
            if (arr[m] >= num) {
                ans = m;
                r = m - 1;
            } else {
                l = m + 1;
            }
        }
        return ans;
    }

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

思路

  • 找到满足条件的最后一个位置

  • 核心:一旦 arr[m] <= num,记录答案并继续向右二分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static int findright(int[] arr, int num) {
        int l = 0, r = arr.length - 1, m = 0;
        int ans = -1;
        while (l <= r) {
            m = l + ((r - l) >> 1);
            if (arr[m] > num) {
                r = m - 1;
            } else {
                ans = m;
                l = m + 1;
            }
        }
        return ans;
    }

寻找峰值元素

  1. 判断【0】位置
  2. 判断【n-1】位置
  3. 检查【1,(n-2)】位置,看中点:
    • 左>中,左侧二分
    • 右>中,右侧二分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public int findPeakElement(int[] nums) {
        int n = nums.length;
        if(nums.length==1){
            return 0;
        }
        if(nums[0]>nums[1]){
            return 0;
        }
        if(nums[n-1]>nums[n-2]){
            return n-1;
        }
        int l = 1,r=n-2,m=0,ans = -1;
        while(l<=r){
            m = l+((r-l)>>1);
            if(nums[m-1]>nums[m]){
                r=m-1;
            }
            else if(nums[m+1]>nums[m]){
                l=m+1;
            }
            else{
                ans = m;
                break;
            }
        }
        return ans;
    }
    public static boolean test(int[] sorteArr, int num) {
        for (int cur : sorteArr) {
            if (cur == num) {
                return true;
            }
        }
        return false;
    }


    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 void main(String[] args) {
        int N = 100;
        int V = 1000;
        int testTimes = 5000;
        System.out.println("测试开始");
        for (int i = 0; i < testTimes; i++) {
            int n = (int) (Math.random() * N);
            int[] arr = randomArray(n, V);
            Arrays.sort(arr);
            int num = (int) (Math.random() * N);
            if (test(arr, num) != exist(arr, num)) {
                System.out.println("出错了!");
            }
        }
    }
    
This post is licensed under CC BY 4.0 by the author.