力扣,
二分查找,边界问题
Top100中重题,思路参考:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/solutions/1980196/er-fen-cha-zhao-zong-shi-xie-bu-dui-yi-g-t9l9/?envType=study-plan-v2&envId=top-100-liked
Python
class Solution:
def countTarget(self, scores: List[int], target: int) -> int:
if len(scores) == 0:
return 0
start = self.search(scores, target)
end = self.search(scores, target + 1) - 1
# print(f"start={start}, end={end}")
return end - start + 1 if start <= end else 0
def search(self, scores, target) -> int:
# 循环不变量:
# scores[l-1] < target
# scores[r+1] >= target
l, r = 0, len(scores) - 1
while l <= r:
mid = (l + r) // 2
if scores[mid] < target:
l = mid + 1 # 范围缩小到 [mid+1, r]
else:
r = mid - 1 # 范围缩小到 [l, mid-1]
# 循环结束后 l = r+1
# 此时 scores[l-1] < target 而 scores[l] = scores[r+1] >= target
# 所以 l 就是第一个 >= target 的元素下标
return l
old
solution1,二分搜索目标值 + 线性扫描
递归版二分搜索,其实不太好
class Solution {
public int countTarget(int[] scores, int target) {
if (scores.length == 0) {
return 0;
}
int[] inxArray = new int[]{-1, -1};
binarySearch(0, scores.length - 1, scores, target, inxArray);
if (inxArray[0] == -1 && inxArray[1] == -1) {
return 0;
}
return inxArray[1] - inxArray[0] + 1;
}
// 递归版二分搜索
public void binarySearch(int start, int end, int[] scores, int target, int[] inxArray) {
if (start > end) {
return;
}
int mid = start + (end - start) / 2;
if (scores[mid] == target) {
inxArray[0] = mid;
inxArray[1] = mid;
while (inxArray[0] - 1 >= 0 && scores[inxArray[0] - 1] == target) {
inxArray[0] -= 1;
}
while (inxArray[1] + 1 < scores.length && scores[inxArray[1] + 1] == target) {
inxArray[1] += 1;
}
return;
} else if (scores[mid] < target) {
start = mid + 1;
binarySearch(start, end, scores, target, inxArray);
} else {
end = mid - 1;
binarySearch(start, end, scores, target, inxArray);
}
}
}
// 迭代版二分搜索
public void binarySearch(int start, int end, int[] scores, int target, int[] inxArray) {
while (start <= end) {
int mid = start + (end - start) / 2;
if (scores[mid] == target) {
inxArray[0] = mid;
inxArray[1] = mid;
while (inxArray[0] - 1 >= 0 && scores[inxArray[0] - 1] == target) {
inxArray[0] -= 1;
}
while (inxArray[1] + 1 < scores.length && scores[inxArray[1] + 1] == target) {
inxArray[1] += 1;
}
return;
} else if (scores[mid] > target) {
end = mid - 1;
} else if (scores[mid] < target) {
start = mid + 1;
}
}
}
solution2,二分搜索边界索引
值得推荐的做法!!!注意:不必检查边界,写法不必像《算法小抄》中繁琐!考虑两种极限情况:
(1)target > scores[n - 1],则左侧索引返回n,右侧索引返回n - 1,最终答案 = (n - 1) - n + 1 = 0;
(2)target < scores[0],则左侧索引返回0,右侧索引返回-1,最终答案 = - 1 - 0 + 1 = 0;
所以最终答案可以非常简洁!!!
class Solution {
public int countTarget(int[] scores, int target) {
if (scores.length == 0) {
return 0;
}
int leftIndex = leftBoundSearch(0, scores.length - 1, scores, target);
int rightIndex = rightBoundSearch(0, scores.length - 1, scores, target);
return rightIndex - leftIndex + 1;
}
public int leftBoundSearch(int start, int end, int[] scores, int target) {
while (start <= end) {
int mid = start + (end - start) / 2;
if (scores[mid] == target) {
end = mid - 1;
} else if (scores[mid] > target) {
end = mid - 1;
} else if (scores[mid] < target) {
start = mid + 1;
}
}
return start;
}
public int rightBoundSearch(int start, int end, int[] scores, int target) {
while (start <= end) {
int mid = start + (end - start) / 2;
if (scores[mid] == target) {
start = mid + 1;
} else if (scores[mid] > target) {
end = mid - 1;
} else if (scores[mid] < target) {
start = mid + 1;
}
}
return end;
}
}