【重点】【二分查找】剑指offer53:在排序数组中查找数字出现次数

文章讨论了在有序数组中使用二分查找解决计数问题时的边界处理,比较了递归和迭代版本,并提出了一种简化边界检查的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

力扣
二分查找,边界问题
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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值