二分查找,最开始做的时候遇到的一个坑。
按照常规解法使用left与right来标志查找位置,当left与right相等时退出查找。查找结点的位置为left+(right-left)/2,当left+(right-left)/2>target时,说明要查找的值在数轴左侧,要使right左移,反之当left+(right-left)/2<target时,说明要查找的值在数轴右侧,要使left右移。这里移动right与left时有一点问题,最初设想的比较简单,直接使left=left+(right-left)/2,使right=right-(right-left)/2,但是运行时出现个别输入不通过的情况,调试后发现这样移动left与right容易使查找循环进入死循环,因为这里的除运算是不做特殊处理直接去尾的,当找不到时就会出现(right-left)/2=0,从而left与right每次循环都不改变。而正确的移动left与right的方法时left=left+(right-left)/2+1,right=left+(right-left)/2-1,即不直接将中点赋值给left或right,而是赋于其中点的相邻点,这样就避免了除法截断导致的死循环。
这个问题解决后,还有一个问题是数组中存在重复元素,题目要求返回下标最小的结果,所以需要在后面加一个循环来寻找最小下标,这个问题不大。
源码如下。

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 如果目标值存在返回下标,否则返回 -1
     * @param nums int整型vector 
     * @param target int整型 
     * @return int整型
     */
    int search(vector<int>& nums, int target) {
        // write code here
        if(nums.size() == 0){
            return -1;
        }
        int right = nums.size() - 1;
        int left = 0;
        int index = 0;
        while(true){
            if(nums.at(left + (right - left)/2) == target){
                break;
            }
            if(nums.at(left + (right - left)/2) > target){
                right = left + (right - left)/2 - 1;

            }
            else if(nums.at(left + (right - left)/2) < target){
                left = left + (right - left)/2 + 1;
            }
            if(left == right || right < 0 || left >= nums.size()){
                break;
            }
        }
        if(right < 0 || left >= nums.size() || target != nums.at(left + (right - left)/2)){
            return -1;
        }
        else{
            index = left + (right - left)/2;
            while(index != 0 && nums.at(index - 1) == target){
                index--;
            }
            return index;
        }
    }
};