https://blog.csdn.net/2601_95366422/article/details/158774021
上节课链接
一.题目

二.思路讲解
第一步:找二分性
题目给出的数组 records 是升序排列,学号从 0 开始连续,但仅有一位同学缺席,因此数组长度比完整学号数少 1。正常情况下,如果没有人缺席,那么每个位置 i 上的数应该等于 i。缺席发生后,缺席点之后的所有数都会比下标大 1。这样,数组就呈现出一种规律:在缺席位置之前,有 records[i] == i;在缺席位置之后,有 records[i] > i。 这种前后分段的特性,使得我们可以通过比较中间元素的值与它的下标,来判断缺失点位于左侧还是右侧,因此数组具备二分性,可以用二分查找解决。
第二步:采用左端点还是右端点
由于我们需要找到第一个 不满足条件的位置(即第一个下标与值不对应的元素),这是典型的寻找左边界 问题。采用左端点二分查找 模板,中点取向下取整 ,缩进规则为:如果中间位置满足条件(如下标等于值),说明正确部分在左边,目标在右边,left = mid + 1;否则(中间位置不满足条件),说明目标在左边或就是中间位置,right = mid。循环结束时,left 即为第一个不满足条件的位置。
第三步:注意边界情况
如果整个数组所有元素都满足条件(即每个下标都与值对应),说明缺失的值位于数组末尾之外,此时循环结束后 left 会指向数组长度(即最后一个元素的下一个位置)。因此,最终答案应为数组长度,表示缺失的元素就是该下标值。这种情况需要特别处理,确保算法返回正确结果。
三.代码演示
cpp
class Solution {
public:
int takeAttendance(vector<int>& records)
{
int n = records.size()-1;
int left = 0;
int right = n;
while(left < right)
{
int mid = left + (right - left)/2;
if(mid == records[mid])
{
left = mid + 1;
}
else
right = mid;
}
if(records[left] == left)
return left + 1;
return left;
}
};
四.代码讲解
一、循环条件与中点计算
采用 while (left < right) 作为循环条件,而不是 left <= right。这是因为当左右指针相遇时,区间内只剩一个元素,此时无需继续二分 ,可以直接在循环外进行判断。这种写法可以避免死循环,并符合左端点模板的常规写法。
中点计算采用向下取整 的公式,即 mid = left + (right - left) / 2。当区间长度为偶数时,中点会偏向左边 ,这确保了当 left 和 right 相邻时,中点指向 left,从而保证区间能够正确收缩,不会出现 right 原地不动的情况。向下取整 是左端点查找的精髓,也是避免死循环的关键。
二、区间缩进规则
在循环中,根据 records[mid] 与 mid 的比较结果,按照以下规则更新指针:
-
当
records[mid] == mid时 :说明mid位置正常,缺席同学一定在mid的右侧。因为缺席点之后所有元素都会大于下标,而mid及左侧都满足相等,所以可以安全地将左指针移动到mid + 1,即舍弃左半区间。 -
当
records[mid] > mid时 :说明mid位置已经异常(因为正常情况下值应等于下标,现在值更大),那么缺席同学可能在mid左侧,也可能就是mid本身。因此将右指针移动到mid,保留mid在搜索区间内 ,继续向左寻找。注意这里不能用right = mid - 1,因为mid有可能是缺席点,必须保留。
三、循环结束后的处理
循环结束时,left 与 right 相等,此时指向的位置是第一个可能的异常点。但需要进一步确认该位置是否真的异常:
-
如果
records[left] == left,说明当前这个位置仍然是正常的,那么缺席同学应该是下一个学号,即left + 1(因为数组长度比完整学号少1,所以缺失的是最后一个数)。 例如示例2中,数组为 [0,1,2,3,4,5,6,8],循环结束后left指向下标7,而records[7] = 8,不相等,所以直接返回7;若数组为 [0,1,2,3,4,5,6,7] 则所有都相等,循环结束left指向最后一个下标7,且records[7]==7,此时缺失的是8,即left+1。 -
如果
records[left] > left,则当前left就是缺席学号,直接返回left。
四、细节注意
-
循环中比较的是
mid == records[mid],由于数组元素是学号,且升序无重复,因此相等关系只出现在缺席点之前。 -
最后判断条件
records[left] == left用于处理整个数组都正常的情况,此时缺席学号等于数组长度(因为学号从0开始,数组长度为 n,缺席一个,完整学号应为 0~n,所以缺失的是 n)。这里返回left + 1恰好就是数组长度。