每日一道算法题2021-10-13

165 阅读1分钟
JZ37 数字在升序数组中出现的次数
给定一个长度为 n 的非降序数组和一个非负数整数 k ,要求统计 k 在数组中出现的次数
数据范围:1000≤n≤1000,0≤k≤100,数组中每个元素的值满足 1000≤val≤100
要求:空间复杂度 O(1)O(1),时间复杂度 O(logn)O(logn)

/* 第一反应for循环很简单,然后发现大家都是用二分法,于是艰难的看别人的二分法思路
*for循环方案
* 定义一个方法
* 返回值类型 int
* 入参1 数组 入参2元素
* */
public static int getNumFromArray(int[] array, int num) {
    int sameNum = 0;
    for (int j = 0; j < array.length; j++) {
        int i = array[j];
        if (num == i) {
            sameNum += 1;
        }
    }
    return sameNum;
}
/*
* 二分法解决方案
*[1,2,3,3,3,3,4,5],3 输出4
* */
public static int getNumberOfK(int[] array, int k) {
    if (array == null || array.length == 0) {
        // 判断数组元素个数
        return 0;
    }
    int min = lowerBound(array, k);
    int max = higherBound(array, k);
    return max - min + 1;
}

// 计算左边
public static int lowerBound(int[] array, int target) {
    int left = 0;
    int right = array.length;
    while (left < right) {
        /* 从左到右找到第一个和target相同的元素下标
        * [1,2,3,3,3,3,4,5]
        * 二分法思路
        * 第一次
        * left = 0, right = 8, middle = 4
        * array[left] = array[0] = 1
        * array[middle] = array[4] = 3
        * array[middle] = target = 3
        * right = middle = 4
        * 第二次
        * left = 0, right = 4, middle = left + (right - left) / 2 = 2
        * array[middle] = array[2] = 3
        * array[middle] = target = 3
        * right = middle = 2
        * 第三次
        * left = 0, right = 2, middle = left + (right - left) / 2 = 1
        * array[middle] = array[1] = 2
        * array[middle] < target
        * left = middle + 1 = 2
        * 第四次
        * left == right == 2 跳出循环
        * left == 2
        * */
        int middle = left + (right - left) / 2;
        if (array[middle] == target) {
            right = middle;
        } else if (array[middle] > target) {
            right = middle;
        } else {
            // array[middle] < target
            left = middle + 1;
        }
    }
    return left;
}
// 计算右边
public static int higherBound(int[] array, int target) {
    int left = 0;
    int right = array.length;
    while (left < right) {
        /* 从左到右找到最后一个和target相同的元素下标
        * [1,2,3,3,3,3,4,5] target = 3 下标2,3,4,5的元素为3
        * 第一次循环
        * middle = 4
        * (array[middle] = array[4] = 3) == target
        * left = middle + 1 = 5
        * 第二次循环
        * left = 5, right = 8
        * middle = left + (right - left) / 2 = 6
        * array[middle] = array[6] = 4 > target
        * right = middle = 6
        * 第三次循环
        * left = 5, right = 6
        * middle = left + (right - left) / 2 = 5
        * array[middle] = array[5] = 3 == target
        * left = middle + 1 = 6
        * 第四次循环
        * left = 6, right = 6
        * left = right = 6 跳出循环
        * array[middle] = array[5] = 3 == target
        * */
        int middle = left + (right - left) / 2;
        if (array[middle] == target) {
            left = middle + 1;
        } else if (array[middle] > target) {
            right = middle;
        } else {
            // array[middle] < target
            left = middle + 1;
        }
    }
    return left - 1;
}