排序数组 nums中的所有数字 target 形成一个窗口,记窗口的 左 / 右边界 索引分别为 left 和 right ,分别对应窗口左边 / 右边的首个元素。
本题要求统计数字 target 的出现次数,可转化为:使用二分法分别找到 左边界 left 和 右边界 right ,易得数字 target 的数量为 right − left − 1 。
初始化: 左边界 i = 0,右边界 j=nums.length−1 。
循环二分: 当闭区间 [i, j] 无元素时跳出;
计算中点 m = (i + j) / 2(向下取整);
若 nums[m] < target,则 target 在闭区间 [m + 1, j]中,因此执行 i = m + 1;
若 nums[m] > target,则 target 在闭区间 [i, m - 1]中,因此执行 j = m - 1;
若 nums[m] = target ,则右边界 right 在闭区间 [m+1, j] 中;左边界 leftleft 在闭区间 [i, m-1]中。因此分为以下两种情况:
若查找 右边界 right ,则执行 i = m + 1 ;(跳出时 i 指向右边界)
若查找 左边界 left ,则执行 j = m - 1;(跳出时 j 指向左边界)
返回值: 应用两次二分,分别查找 right和 left ,最终返回 right - left - 1即可。
classSolution{publicintsearch(int[] nums,int target){// 搜索右边界 rightint i =0, j = nums.length -1;while(i <= j){int m =(i + j)/2;if(nums[m]<= target) i = m +1;else j = m -1;}int right = i;// 若数组中无 target ,则提前返回,这一行代码可有可无。if(j >=0&& nums[j]!= target)return0;// 搜索左边界 left
i =0; j = nums.length -1;while(i <= j){int m =(i + j)/2;if(nums[m]< target) i = m +1;else j = m -1;}int left = j;return right - left -1;}}
优化,以上代码显得比较臃肿(两轮二分查找代码冗余),改为寻找target - 1的右边界。
classSolution{publicintsearch(int[] nums,int target){returnfindRight(nums, target)-findRight(nums, target -1);}intfindRight(int[] nums,int tar){int i =0, j = nums.length -1;while(i <= j){int m =(i + j)/2;if(nums[m]<= tar) i = m +1;else j = m -1;}return i;}}