自学内容网 自学内容网

【6.4】位运算-判断是否存在重复元素

一、题目

        给定一个整数数组,判断 是否存在重复元素 。如果存在一值在数组中 出现至少两次 ,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。

示例 1:
输入: [ 1 , 2 , 3 , 1 ]
输出: true

示例 2:
输入: [ 1 , 2 , 3 , 4 ]
输出: false

示例 3:
输入: [ 1 , 1 , 1 , 3 , 3 , 4 , 3 , 2 , 4 , 2 ]
输出: true

二、解题思路

        这道题可以采用暴力求解的方法,即使用两个 for 循环进行每两两比较,不过这种方式的复杂度较高,这里就不给出具体代码了。除了暴力求解之外,其实还有一种方式,估计大家都能想到,那就是先对数组进行排序。排序之后,如果存在相同的元素,它们肯定是相邻的,然后我们再对排序后的数组进行前后两两比较即可。

        我们也可以使用 set 集合来解决这个问题。因为 set 集合中不能包含重复元素,如果有重复元素就会将其替换掉。我们把数组中的元素全部添加到集合 set 中,如果 set 的 size 与数组的长度不相等,那就说明存在重复的元素。

        我们主要探讨用位运算来解决此问题。这里要判断数组中是否有重复的数字,而判断是否有重复数字,最容易想到的一种解决方式就是使用位运算。c++中,long 类型在64位系统中占 64 位,每一位可以表示一个数字,所以一个 long 类型可以用来表示 64 个数字。但是要想在32位系统做到代码通用,可以使用64位整数类型表示 64 个数字。

        实现方式比较简单,我们遍历数组中的所有元素,查看对应的位置是否为 1,如果是 1,说明存在重复的元素,我们直接返回 true。否则,把数组中元素对应的位置变为 1。比如数组中有个元素是 3,我们就把上面第 3 个位置变为 1。

        原理比较简单,但数组中元素的值不可能都小于 64,所以我们需要分组。根据数组的范围每 64 分为一组。因为数组中还可能有负数存在,所以我们第一步先找出数组中的最大值和最小值。每个元素的位置需要减去最小值来确定。举个例子,比如数组元素为 [3,-10,65,30],最小值是 -10,那么每个元素的位置就是 [13,0,75,40]。我们可以看到 0、13、40 都是小于 64 的,所以它们在第一组里面,而 75 在第二组里面。我们可以申请一个 uint64_t 类型的数组 bitmap,其中 bitmap [0] 相当于第一组,bitmap [1] 相当于第二组……

三、代码实现

#include <iostream>
#include <vector>
#include <cstdint> // 包含uint64_t类型

bool containsDuplicate(const std::vector<int>& nums) {
    // 找出数组中的最大值和最小值
    int min = nums[0];
    int max = min;
    for (int num : nums) {
        if (num < min) min = num;
        if (num > max) max = num;
    }
    
    // 计算数组中最大值和最小值的差值,目的是要确定位图的长度
    int distant = max - min + 1;
    int bitmapSize = (distant - 1) / 64 + 1;
    std::vector<uint64_t> bitmap(bitmapSize, 0);
    
    for (int num : nums) {
        // 根据当前数字到最小数字的长度,定位到当前数字在位图中的位置
        int tmp = num - min;
        if ((bitmap[tmp / 64] & (1ULL << (tmp % 64))) != 0)
            return true;
        
        // 如果不存在重复的数字,就把当前这个位置变为1
        bitmap[tmp / 64] |= 1ULL << (tmp % 64);
    }
    
    return false;
}

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 1};
    std::cout << "Contains duplicate: " << (containsDuplicate(nums) ? "true" : "false") << std::endl;
    return 0;
}

        这里需要注意,在进行 1ULL << (tmp % 64)) 运算时,1 后面一定要加上大写的 ULL,表示这是64位的无符号长长整数类型,ULL后缀确保了常量的位宽为64位,不管在多少位系统下。如果不加的话,1 就会被视为 int 类型。当数字比较大的时候,结果就会出现错误。


原文地址:https://blog.csdn.net/linshantang/article/details/143738182

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!