问题描述


难度:简单


给定一个整数数组,判断是否存在重复元素。


如果存在一值在数组中出现至少两次,函数返回 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




位运算解决



这题说的是判断数组中是否有重复的数字,判断是否有重复数字,最容易想到的一种解决方式就是使用位运算。比如说在java中long类型占64位,每一位可以表示一个数字,所以一个long类型可以用它表示64个数字。如下图所示

607,位运算等多种方式判断是否存在重复元素_重复元素

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


原理比较简单,但数组中元素的值不可能都小于64,所以我们需要分组,根据数组的范围每64分为一组。因为数组中还可能有负数存在,所以我们
第一步先找出数组中的最大值和最小值每个元素的位置需要减去最小值来确定。举个例子,比如数组元素

[3,-10,65,30]

最小值是-10,那么每个元素的位置就是

[13,0,75,40]

我们看到0,13,40都是小于64的,所以他们是在第一组里面的,而75是在第二组里面的。我们可以申请一个long类型的数组long[]bitmap,其中bitmap[0]相当于第一组,bitmap[1]相当于第二组……


我们来看下代码。

public boolean containsDuplicate(int[] nums) {
//找出数组中的最大值和最小值
int min = nums[0];
int max = min;
for (int num : nums) {
min = Math.min(min, num);
max = Math.max(max, num);
}
//计算数组中最大值和最小值的差值,目的是要确定位图的长度
int distant = max - min + 1;
long[] bitmap = new long[(distant - 1) / 64 + 1];
for (int num : nums) {
//根据当前数字到最小数字的长度,定位到当前数字在位图
//中的位置。判断这个位置是不是1,如果是1,说明存在
//重复的数字
int tmp = num - min;
if ((bitmap[tmp / 64] & (1L << (tmp % 64))) != 0)
return true;
//如果不存在重复的数字,就把当前这个位置变为1
bitmap[tmp / 64] |= 1L << (tmp % 64);
}
return false;
}

这里要注意1L << (tmp % 64)运算的时候,1后面一定要加个大写的L,表示是long类型。如果不加,1就是int类型,当数字比较大的时候结果就会出现错误。我刚开始写的时候感觉代码没问题,但当数字大的时候运行始终不通过,后来发现错在了这个地方。



先排序



这题还可以使用暴力求解,就是使用两个for循环每两两比较,但复杂度比较高,代码就不在写了。除了暴力求解,其实还有一种方式估计大家都已经想到了,就是先对数组进行排序,排序之后如果有相同的元素,他们肯定是挨着的,然后我们在对排序后的数组前后两两比较即可,看下代码。

public boolean containsDuplicate(int[] nums) {
//先对数组进行排序
Arrays.sort(nums);
for (int ind = 1; ind < nums.length; ind++) {
//前后两两比较,如果有相同的直接返回true
if (nums[ind] == nums[ind - 1]) {
return true;
}
}
return false;
}



使用Set集合



除了上面介绍的两种实现方式以外,我们还可以使用set集合。因为set集合中是不能包含重复元素的,如果有重复元素就会把它给替换掉,我们把数组中的元素全部添加到集合set中,如果set的size不等于数组的长度,说明有重复的,来看下代码

public boolean containsDuplicate(int[] nums) {
final Set<Integer> set = new HashSet<>();
//把数组中的元素全部添加到集合set中
for (int num : nums) {
set.add(num);
}
//如果有重复的元素在set中就会被替换掉,导致
//size变小,所以如果set的size不等于数组的
//长度,说明有重复的元素
return set.size() != nums.length;
}

上面代码中如果有重复的元素,add方法会返回false,如果没有重复的元素,add方法会返回true,所以我们没必要把数组中的元素全部添加到集合set中。我们一个个添加,如果add方法返回false,说明有重复的元素,直接返回true即可,看下代码。

public boolean containsDuplicate(int[] nums) {
Set<Integer> set = new HashSet<>();
//把数组中的元素放到集合set中
for (int num : nums) {
//如果有重复的元素add方法返回false,
//否则返回true
if (!set.add(num)) {
return true;
}
}
return false;
}