287. 寻找重复数

287. 寻找重复数

题意

在这里插入图片描述


解一

  • 二分答案

简单解释,官解也很详细。

定义 d p [ i ] {dp[i]} dp[i] 表示 n u m s [ i ] nums[i] nums[i] < = i <=i <=i 的数的个数。

看这样一个序列:
nums:3 1 3 4 2
dp: 1 2 4 5

性质:自重复的数起,之后所有 d p [ i ] dp[i] dp[i] > i >i >i ,之前都 < = i <=i <=i

在看这样一个序列:
nums:3 1 3 4 3
dp: 1 1 4 5

尽管重复了多次,但是还是满足上面的性质。

那么我们二分答案,统计 < = m i d <=mid <=mid 的数的个数,如果个数 > m i d >mid >mid ,说明是在右侧,可能是答案, r = m i d r=mid r=mid,否则 l = m i d + 1 l=mid+1 l=mid+1

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int n = nums.size();
        int l = 1, r = n;
        while(l < r) {
            int mid = (l + r) >> 1;
            auto f = [=](int x){
                int res = 0;
                for (int i = 0; i < n; i++)
                    if(nums[i] <= x) res += 1;
                return res <= x;
            };
            if(f(mid)) l = mid + 1;
            else r = mid;
        }
        return r;
    }
};
/* 一旦加上了重复的数,那么之后一定更大
// 二分check,更小就不要,说明在答案左,l向右调整。
    nums: 1 3 2 4 2

    [1234]: 1 3 4 5
        mid: 3 -> res = 4, r = 3
        mid: 2 -> res = 3, r = 2
        mid: 1 -> res = 1, l = 2
        l == r = 2
    
    nums: 1 3 2 3 4 3

    [1234]: 1 2 5 6
        mid: 3 -> res = 5, r = 3
        mid: 2 -> res = 2, l = 3
        l == r = 3
*/

解二

  • 二进制

  • 重复一次

    • 重复的数为 x x x [ 1 , n ] [1,n] [1,n] 都各出现一次,外加 x x x 多出现一次。
    • 统计二进制下 1 1 1 的个数,原序列相比 [ 1 , n ] [1,n] [1,n] 序列,多出的 1 1 1 都是由 x x x 产生。
  • 重复多次

    • 重复的数为 x x x x x x 代替 [ 1 , n ] [1,n] [1,n] 中没出现的数 y y y,外加 x x x 多出现一次。

    • 四种情况:00、01、10、11(以下x,y表示某一位为1或0)

      • x = 0 , y = 0 x=0,y=0 x=0,y=0:<=
      • x = 0 , y = 1 x=0,y=1 x=0,y=1:<=
      • x = 1 , y = 0 x=1,y=0 x=1,y=0:>
      • x = 1 , y = 1 x=1,y=1 x=1,y=1:>

所以,对于 > > > 的情况,将该位 1 1 1 加上即可。

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int n = nums.size();
        int res = 0;
        for (int i = 0; i < 31; i++) {
            int x = 0, y = 0;
            for (int j = 0; j < n; j++) {
                if(nums[j] & (1 << i)) x += 1;
            }
            for (int j = 1; j < n; j++) {
                if(j & (1 << i)) y += 1;
            }
            if(x > y) res |= (1 << i);
        }
        return res;
    }
};

解三

  • 快慢指针

n + 1 n + 1 n+1个数, [ 1 , n ] [1,n] [1,n] 范围内。每个位置 i i i 连边 i − i- i> n u m s [ i ] nums[i] nums[i]

3 1 3 4 2
0 1 2 3 4

建边:0->3, 1->1, 2->3, 3->4, 4->2

在这里插入图片描述
明显重复的 3 3 3 是环的入口。

又点 0 0 0 只可能有出边,不可能自环。所以定义快慢指针 l , r l,r l,r 都从 0 0 0 开始。

然后就是环形链表找入口的常规解法:

  • 慢一步、快二步
  • 慢置为 0 0 0
  • 慢一步、快一步
class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int l = 0, r = 0;
        do {
            l = nums[l];
            r = nums[nums[r]];
        } while(l != r);
        l = 0;
        while(l != r) {
            l = nums[l];
            r = nums[r];
        }
        return l;
    }
};
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
根据引用,给出一个数组,找出数组的第k大的数的方法是基于快速排序的思路。具体步骤如下: 1. 定义一个函数`quickFind`,参数为数组`arr`、左边界`left`、右边界`right`和要找的第k大的数`k`。 2. 在函数内部,使用快速排序的思路进行处理。首先选择一个基准值`key`,将其设为数组的第一个元素`arr[left]`。 3. 使用两个指针`i`和`j`,分别指向数组的左边界和右边界。 4. 在一个循环中,将比基准值大的元素移到数组的右边,比基准值小的元素移到数组的左边。 5. 循环结束后,将基准值放到合适的位置,使得基准值的左边都是比其小的数,右边都是比其大的数。 6. 根据基准值的位置和k的关系,判断第k大的数在基准值的左边还是右边。 - 若基准值所在的位置右边的数据小于k-1个,说明第k大的数在基准值的左边序列中,需要对左边的数据进行快排,并找到第k-big-1大的数据。 - 若基准值所在的位置右边的数据大于k-1个,说明第k大的数在基准值的右边序列中,需要对右边的数据进行快排并找到第k大的数。 - 若基准值所在的位置右边的数据正好是k-1个,说明基准值就是第k大的数,返回基准值。 7. 在主函数中调用`quickFind`函数,给出数组和要找的第k大的数。 根据引用和引用中的代码实现,给定一个整数数组a和要找的第K大的数,可以使用快速排序的原理来寻找第K大的数。具体步骤如下: 1. 定义一个函数`quickSort`,参数为数组`arr`、起始位置`start`、结束位置`end`和要找的第K大的数`k`。 2. 在函数内部,设置递归结束条件,若起始位置大于结束位置,则返回-1。 3. 设置两个指针`i`和`j`,分别指向起始位置和结束位置,以及一个基准值`base`,将其设为数组的起始位置的元素`arr[start]`。 4. 在一个循环中,将比基准值大的元素移到数组的右边,比基准值小的元素移到数组的左边。 5. 循环结束后,将基准值放到合适的位置,使得基准值的左边都是比其小的数,右边都是比其大的数。 6. 根据基准值的位置和k的关系,判断第k大的数在基准值的左边还是右边。 - 若基准值的位置等于`arr.length - k`,说明找到了第K大的数,返回该值。 - 若基准值的位置小于`arr.length - k`,说明第K大的数在基准值的右边序列中,继续递归调用`quickSort`函数,在右边序列中寻找第K大的数。 - 若基准值的位置大于`arr.length - k`,说明第K大的数在基准值的左边序列中,继续递归调用`quickSort`函数,在左边序列中寻找第K大的数。 7. 在主函数中调用`quickSort`函数,给出数组、起始位置、结束位置和要找的第K大的数。 综上所述,根据以上的方法和步骤,可以找到数组的第K大的数。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++——寻找第k大的数](https://blog.csdn.net/ldm_666/article/details/117229499)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [寻找数组中第k大的数](https://blog.csdn.net/m0_37671741/article/details/108304136)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ღCauchyོꦿ࿐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值