zoukankan      html  css  js  c++  java
  • LeetCode | 287. 寻找重复数

    特别感谢LeetCode大佬陈牧远的科普知识

    给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

    示例 1:

    输入: [1,3,4,2,2]
    输出: 2
    

    示例 2:

    输入: [3,1,3,4,2]
    输出: 3
    

    说明:

    1. 不能更改原数组(假设数组是只读的)。
    2. 只能使用额外的 O(1) 的空间。
    3. 时间复杂度小于 O(n2) 。
    4. 数组中只有一个重复的数字,但它可能不止重复出现一次。

    解析:

    【笔记】这道题(据说)花费了计算机科学界的传奇人物Don Knuth 24小时才解出来。并且我只见过一个人(注:Keith Amling)用更短时间解出此题。

    快慢指针,一个时间复杂度为O(N)的算法。

    • 其一,对于链表问题,使用快慢指针可以判断是否有环。
    • 其二,本题可以使用数组配合下标,抽象成链表问题。但是难点是要定位环的入口位置。

    举个例子:nums = [2,5, 9 ,6,9,3,8, 9 ,7,1],构造成链表就是:2->[9]->1->5->3->6->8->7->[9],也就是在[9]处循环。

    • 其三,快慢指针问题,会在环内的[9]->1->5->3->6->8->7->[9]任何一个节点追上,不一定是在[9]处相碰,事实上会在7处碰上。
    • 其四,必须另起一个for循环定位环入口位置[9]。这里需要数学证明。

    对“其四”简单说明一下,既然快慢指针在环内的某处已经相碰了。那么,第二个for循环遍历时,res指针还是在不停的绕环走,但是必定和i指针在环入口处相碰。

    双指针:

    class Solution {
    public:
        int findDuplicate(vector<int>& nums) {
            int res = 0;
            for (int fast = 0; res != fast || fast == 0;){
                res = nums[res];
                fast = nums[nums[fast]];
            }
            cout << res;
            for (int i = 0; res != i; i = nums[i]){
                res = nums[res];
            }
            return res;
        }
    };
    

    二分查找

    class Solution {
    public:
        int findDuplicate(vector<int> &nums) {
            int len = nums.size();
            int left = 0;
            int right = len - 1;
    
            while (left < right) {
                int mid = left + (right - left) / 2;
    
                int cnt = 0;
                for (int num:nums) {
                    if (num <= mid) {
                        cnt++;
                    }
                }
    
                // 根据抽屉原理,小于等于 4 的数的个数如果严格大于 4 个,
                // 此时重复元素一定出现在 [1, 4] 区间里
    
                if (cnt > mid) {
                    // 重复的元素一定出现在 [left, mid] 区间里
                    right = mid;
                } else {
                    // if 分析正确了以后,else 搜索的区间就是 if 的反面
                    // [mid + 1, right]
                    // 注意:此时需要调整中位数的取法为上取整
                    left = mid + 1;
                }
            }
            return left;
        }
    };
    
  • 相关阅读:
    CentOS查看CPU信息、位数、多核信息
    Linux常用命令大全
    chmod命令详细用法
    tar命令的详细解释
    yum和rpm命令详解
    LeetCode 241. Different Ways to Add Parentheses
    LeetCode 139. Word Break
    LeetCode 201. Bitwise AND of Numbers Range
    LeetCode 486. Predict the Winner
    LeetCode 17. Letter Combinations of a Phone Number
  • 原文地址:https://www.cnblogs.com/RioTian/p/12534575.html
Copyright © 2011-2022 走看看