二分搜索的变形
总结一下:
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
作者:Ryanjie
出处:http://www.cnblogs.com/ryanjan/
本文版权归作者和博客园所有,欢迎转载。转载请在留言板处留言给我,且在文章标明原文链接,谢谢!
如果您觉得本篇博文对您有所收获,觉得我还算用心,请点击右下角的 [推荐],谢谢!