• 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;
        }
    };
    
  • 相关阅读:
    ONNX 开始
    Linux 性能相关工具
    Prometheus + Grafana 快速上手
    RTSP 流相关工具介绍
    TensorFlow Serving
    TensorFlow 的 JupyterLab 环境
    【排序算法动画解】排序介绍及冒泡排序
    上课老师提问我什么是二叉查找树,我把这些动图拿了出来,动图图解及代码实现。
    如何找东西?查找算法之顺序查找和二分查找详解
    【数据结构和算法:简单方法】谈一谈优先队列的实现
  • 原文地址:https://www.cnblogs.com/RioTian/p/12534575.html
Copyright © 2020-2023  润新知