二分搜索的变形

总结一下:

1、数组中的数不重复,最简单的情况

1、最普通的二分查找:leetcode704
/**
 * 二分查找:nums 中的所有元素是不重复的
 */
class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length - 1;  // 左闭右闭
        while (left < right) {
            int mid = left + ((right - left) >> 1);
            if (nums[mid] == target) return mid;
            else if (nums[mid] > target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }   // 终止条件left == right,后面就不用混淆ans究竟用left还是right了
        // nums[left]还未判断
        return nums[left] == target? left: -1;
    }
}

 

2、二分查找:寻找第一个等于target和最后一个等于target的元素 leetcode34(数组中的元素有重复)

class Solution {
    public int[] searchRange(int[] nums, int target) {
        if (nums == null || nums.length == 0) return new int[]{-1, -1};
        return new int[]{firstEquals(nums, target), lastEquals(nums, target)};
    }

    // 寻找第一个等于target的元素
    public int firstEquals(int[] nums, int target) {
        int left = 0, right = nums.length - 1;  // 左闭右闭
        while (left < right) {
            int mid = left + ((right - left) >> 1); // 向下取整
            if (nums[mid] == target) {
                right = mid;    // [target target]
            } else if (nums[mid] > target) {
                right = mid;    // [2 2] target = 1 宁可少缩放一点,也不能越界,不能写成mid-1
            } else {
                left = mid + 1;
            }
        }
        // 特判 nums[left]
        if (nums[left] == target && (left == 0 || nums[left-1] < target)) return left;
        return -1;  // 没找到
    } 

    // 寻找最后一个等于target的元素
    public int lastEquals(int[] nums, int target) {
        int left = 0, right = nums.length - 1;  // 左闭右闭
        while (left < right) {
            int mid = left + ((right - left + 1) >> 1);    // 向上取整
            if (nums[mid] == target) {  // [target target]
                left = mid;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] < target) {
                left = mid; // // [2 2] target = 3 宁可少缩放一点,也不能越界,不能写成mid+1 
            }
        }
        if (nums[left] == target && (left == nums.length - 1 || nums[left+1] > target)) return left;
        return -1;  // 没找到
    }
}

 

3、查找第一个大于等于给定值的元素 leetcode35

class Solution {
    public int firstLargeOrEquals(int[] nums, int target) {
        int left = 0, right = nums.length - 1;  // 左闭右闭
        while (left < right) {
            int mid = left + ((right - left) >> 1);  // 向下取整
            if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid;    // 查找大于等于给定元素 mid属于解空间
            }
        }
        if (nums[left] >= target && (left == 0 || nums[left-1] < target)) return left;  // 大于等于给定元素
        return -1;    // 没找到说明都比target小 (有的题目会让 放到最后即可)
    }
}

 

4、查找最后一个小于等于给定值的元素

private int lastLessOrEquals(int[] arr, int target) {
    int l = 0, r = arr.length - 1;
    while (l < r) {
        int mid = l + ((r - l + 1) >> 1);  // 向上取整
        if (arr[mid] > target) r = mid - 1;
        else l = mid; // 查找小于等于target的位置,
    }
    if (arr[l] <= target && (l == arr.length - 1 || arr[l + 1] > target)) return l; // <=
    return -1;  // 没找到说明都比target大 (有的题目会让 放到第一个位置)
}

例题:https://leetcode-cn.com/problems/online-election/submissions/

class TopVotedCandidate {
    List<int[]> list = new ArrayList<>();    

    public TopVotedCandidate(int[] persons, int[] times) {
       Map<Integer, Integer> map = new HashMap<>(); // Map维护每个人的票数
       int val = 0; //  表示当前的最大票数
       for (int i = 0; i < times.length; i++) {
           map.put(persons[i], map.getOrDefault(persons[i], 0) + 1);    // 先投票
           if (map.get(persons[i]) >= val) {
               val = map.get(persons[i]);
               list.add(new int[]{times[i], persons[i]});
           }
       }
    }
    
    public int q(int t) {   // 找到最后一个小于等于t的时刻 <=
        int left = 0, right = list.size() - 1;
        while (left < right) {
            int mid = left + ((right - left + 1) >> 1); // 向上取整
            if (list.get(mid)[0] > t) {
                right = mid - 1;
            } else {
                left = mid;
            }
        }

        if (list.get(left)[0] <= t && (left == list.size()-1 || list.get(left+1)[0] > t)) return list.get(left)[1];
        else return 0;
    }
}

/**
 * Your TopVotedCandidate object will be instantiated and called as such:
 * TopVotedCandidate obj = new TopVotedCandidate(persons, times);
 * int param_1 = obj.q(t);
 */

 

 

 

 

5、lower_bound和upper_bound

 

public int lowerBound(int[] nums, int target) { // >=
        int left = 0, right = nums.length-1;
        while (left < right) {
            int mid = left + ((right-left) >> 1);
            if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        if (nums[left] >= target && (left == 0 || nums[left-1] < target)) return left;
        return nums.length; // 都比target小,返回最后一个
    }

    public int upperBound(int[] nums, int target) {    // >
        int left = 0, right = nums.length-1;
        while (left < right) {
            int mid = left + ((right-left) >> 1);
            if (nums[mid] <= target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        if (nums[left] > target && (left == 0 || nums[left-1] <= target)) return left;
        return nums.length;
    }

 lower_bound和upper_bound的另一种写法

https://www.cnblogs.com/1pha/p/8448674.html

 

posted @ 2021-11-21 17:31  Peterxiazhen  阅读(80)  评论(0编辑  收藏  举报