uacs2024

导航

leetcode 287. 寻找重复数

287. 寻找重复数

只会暴力超时🤡

法一:二分查找

官方题解:

class Solution {
public:
//先累计大小在 [1,⌊n/2⌋]之间的数字个数,如果重复数在这个范围内,则个数 > ⌊n/2⌋,
//否则可确定区间 (⌊n/2⌋,n]内存在重复数。即可通过二分查找求解。
    int findDuplicate(vector<int>& nums) {
        // 获取数组的长度
        int n = nums.size();
        // 初始化二分查找的左右边界,左边界为1,右边界为数组长度减1
        int l = 1, r = n - 1;
        // 初始化答案变量,用于存储可能的重复数
        int ans = -1;
        
        // 当左边界小于等于右边界时,继续循环
        while (l <= r) {
            // 计算中间值,相当于(l + r) / 2,但使用位运算避免溢出
            int mid = (l + r) >> 1;
            // 初始化计数器,用于统计数组中小于等于mid的元素个数
            int cnt = 0;
            
            // 遍历数组,统计小于等于mid的元素个数
            for (int i = 0; i < n; ++i) {
                if (nums[i] <= mid)  cnt++;               
            }
            
            // 根据统计结果调整查找范围
            // 如果cnt <= mid,说明重复数在右半部分,移动左边界到mid+1
            if (cnt <= mid)  l = mid + 1;
            
            // 否则,说明重复数在左半部分,移动右边界到mid-1,并记录当前mid为可能的重复数
            else {
                r = mid - 1;
                ans = mid;
            }
        }
        
        // 返回找到的重复数
        return ans;
    }
};

另一种写法

class Solution {
public:
//先累计大小在 [1,⌊n/2⌋]之间的数字个数,如果重复数在这个范围内,则个数 > ⌊n/2⌋,
//否则可确定区间 (⌊n/2⌋,n]内存在重复数。即可通过二分查找求解。
    int findDuplicate(vector<int>& nums) {
        // 初始化查找范围的最小值为1,因为数组中的数字从1开始
        int min = 1;
        // 初始化查找范围的最大值为数组的长度,因为数组长度是n+1,数字范围是1到n
        int max = nums.size() - 1;

        // 当最小值小于最大值时,继续查找
        while (min < max) {
            // 计算中间值,作为当前查找范围的中间点
            int mid = (min + max) / 2;

            // 统计数组中在[min, mid]范围内的数字个数
            int cnt = 0;
            for (int &v : nums) {
                if (v >= min && v <= mid)  cnt++;                
            }

            // 如果统计的个数超过范围的长度,说明该范围内有重复数字
            if (cnt > mid - min + 1) {
                // 将最大值调整为mid,缩小查找范围到左半部分
                max = mid;
            } else {
                // 否则,将最小值调整为mid+1,查找范围移动到右半部分
                min = mid + 1;
            }
        }

        // 当循环结束时,min即为重复的数字
        return min;
    }
};

 法二:快慢指针,思路类似   leetcode142. 环形链表 II

 

class Solution {
public:
//环的存在:由于数组中存在重复数字,可以将数组视为一个链表结构,其中重复数字是环的入口点。slow 和 fast 指针最终会在环内相遇。
//环的入口点:当 slow 和 fast 相遇后,从起点和相遇点分别移动两个指针,每次一步,它们会在环的入口点相遇。这是因为从起点到入口点的距离等于从相遇点到入口点的距离。
    int findDuplicate(vector<int>& nums) {
        // 初始化两个指针,slow和fast,都从数组的起始位置开始
        int slow = 0;
        int fast = 0;
        
        // 移动slow指针一步,fast指针两步
        slow = nums[slow];
        fast = nums[nums[fast]];
        
        // 当slow和fast相遇时,说明找到了环
        while (slow != fast) {
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
        
        // 初始化另一个指针pre1从起点开始,pre2从相遇点开始
        int pre1 = 0;
        int pre2 = slow;
        
        // 当pre1和pre2相遇时,相遇点就是重复的数字
        while (pre1 != pre2) {
            pre1 = nums[pre1];
            pre2 = nums[pre2];
        }
        
        // 返回重复的数字
        return pre1;
    }
};

 

posted on 2025-03-13 20:23  ᶜʸᵃⁿ  阅读(49)  评论(0)    收藏  举报