文章目录
1 题目
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:2 <= n <= 100000
2 知识点
2.1 Map接口和HashMap类
1、Map 是 Key-Value 对映射的抽象接口,该映射不包括重复的键,即一个键对应一个值。HashMap 是 Java Collection Framework 的重要成员,也是Map族(如下图所示)中我们最为常用的一种。简单地说,HashMap 是基于哈希表的 Map 接口的实现,以 Key-Value 的形式存在,即存储的对象是 Entry (同时包含了 Key 和 Value) 。在HashMap中,其会根据hash算法来计算key-value的存储位置并进行快速存取。特别地,HashMap最多只允许一条Entry的键为Null(多条会覆盖),但允许多条Entry的值为Null。此外,HashMap 是 Map 的一个非同步的实现。
2、HashMap的containsKey方法
(1)containsKey() 方法检查 hashMap 中是否存在指定的 key 对应的映射关系。
(2)语法:
hashmap.containsKey(Object key)
注:hashmap 是 HashMap 类的一个对象。
(3)参数:
key - 键
(4)返回值:如果 hashMap 中是否存在指定的 key 对应的映射关系返回 true,否则返回 false。
(5)例子:
hashmap.containsKey(num)
3、HashMap的put方法
(1)put() 方法将指定的键/值对插入到 HashMap 中。
(2)语法:
hashmap.put(K key,V value)
注:hashmap 是 HashMap 类的一个对象。
(3)参数:
key - 键
value - 值
(4)返回值:如果插入的 key 对应的 value 已经存在,则执行 value 替换操作,返回旧的 value 值,如果不存在则执行插入,返回 null。
(5)例子:
hashmap.put(num, 0);
2.2 Set接口和HashSet类
1、Set:一个不包含重复元素的 collection。
- 更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。
- 父接口:Collection
- 实现类:HashSet,TreeSet,LinkedHashSet
- set特点:无序且唯一,只能添加一个null元素
2、HashSet:此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。
- 它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。
- 父接口:List
- 父类:AbstractSet
- 子类:LinkedHashSet
- 特点:无序且唯一,允许使用null元素,但是只能有一个null元素
- 注意,此实现不是同步的。
3、HashSet的contains方法
(1)如果此set包含指定的元素,则返回true 。 更正式地说,返回true当且仅当此set包含的元素e ,使得Objects.equals(o, e) 。
(2)语法:
public boolean contains (Object o)
(3)参数:o - 要测试其在此集合中的存在的元素
(4)返回值:true如果此set包含指定的元素
(5)例子:
set.contains(num)
4、HashSet的add方法
(1)如果指定的元素尚不存在,则将其添加到此集合中。 更正式地,将指定的元素e这一套,如果set不包含元素e2使得Objects.equals(e, e2) 。 如果此set已包含该元素,则调用将保持set不变并返回false 。
(2)语法:
public boolean add(E e)
(3)参数:e - 要添加到此集合的元素
(4)返回值:true如果此集合尚未包含指定的元素
(5)例子:
set.add(num)
3 各种解法
3.1 桶排序,byte作桶
桶排序,时间复杂度和空间复杂度都是o(n)
每个桶占用1个字节,好处是很节省空间,好像和Boolean没啥区别。
public class FindRepeatNumber01 {
public int findRepeatNumber(int[] nums) {
byte flag[] = new byte[nums.length];
for (int i = 0; i < nums.length; i++) {
if (flag[nums[i]] == 1) {
return nums[i];
} else {
flag[nums[i]] = 1;
}
}
return -1;
}
}
3.2 桶排序,boolean作桶
桶排序,时间复杂度和空间复杂度都是o(n)
每个桶占用1个字节,优点是方便理解。
public class FindRepeatNumber02 {
public int findRepeatNumber(int[] nums) {
boolean flag[] = new boolean[nums.length];
for (int i = 0; i < nums.length; i++) {
if (flag[nums[i]] == true) {
return nums[i];
} else {
flag[nums[i]] = true;
}
}
return -1;
}
}
3.3 桶排序,int作桶
桶排序,时间复杂度和空间复杂度都是o(n)
每个桶占用4个字节,优点是还可以判断哪个重复的最多。
public class FindRepeatNumber03 {
public int findRepeatNumber(int[] nums) {
int[] count = new int[nums.length];
for (int temp : nums) {
if (++count[temp] > 1) {
return temp;
}
}
return -1;
}
}
3.4 int桶的另一种写法
public class FindRepeatNumber04 {
public int findRepeatNumber(int[] nums) {
int[] count = new int[nums.length];
for (int temp : nums) {
count[temp]++;
if (count[temp] > 1) {
return temp;
}
}
return -1;
}
}
3.5 置换法
public class FindRepeatNumber05 {
public int findRepeatNumber(int[] nums) {
int temp;
for (int i = 0; i < nums.length; i++) {
while (nums[i] != i) {
if (nums[i] == nums[nums[i]]) {
return nums[i];
}
temp = nums[i];
nums[i] = nums[temp];
nums[temp] = temp;
}
}
return -1;
}
}
3.6 使用map的方式
public class FindRepeatNumber06 {
public int findRepeatNumber(int[] nums) {
Map<Integer, Integer> hashmap = new HashMap<>();
for (int num : nums) {
if (hashmap.containsKey(num)) {
return num;
}
hashmap.put(num, 0);
}
return -1;
}
}
3.7 使用set的add判断
public class FindRepeatNumber07 {
public int findRepeatNumber(int[] nums) {
Set<Integer> set = new HashSet<Integer>();
for (int num : nums) {
if (!set.add(num)) {
return num;
}
}
return -1;
}
}
3.8 使用set的add判断的另一种写法
public class FindRepeatNumber08 {
public int findRepeatNumber(int[] nums) {
Set<Integer> set = new HashSet<Integer>();
int repeat = -1;
for (int num : nums) {
if (!set.add(num)) {
repeat = num;
break;
}
}
return repeat;
}
}
3.9 使用set的contains判断
public class FindRepeatNumber09 {
public int findRepeatNumber(int[] nums) {
Set<Integer> dic = new HashSet<>();
for (int num : nums) {
if (dic.contains(num)) {
return num;
}
dic.add(num);
}
return -1;
}
}
参考资料:
1、本题来源于力扣 (LeetCode)《剑指 Offer 03. 数组中重复的数字》。
2、野生金的JAVA简洁版本 10行代码
3、derrick_sun的原地置换,时间空间100%
4、郁郁雨的剑指 Offer 03. 数组中重复的数字,哈希表解法 + 原地交换解法~
5、力扣官方题解的面试题03. 数组中重复的数字
6、Krahets的剑指 Offer 03. 数组中重复的数字(哈希表 / 原地交换,清晰图解)
7、书呆子Rico的Map 综述(一):彻头彻尾理解 HashMap
8、司天宏的java set类及其子类—HashSet和LinkedHashSet类
9、菜鸟教程的Java HashMap以及Java HashSet
另:一些说明
1、本博客仅用于记录学习和交流,欢迎大家瞧瞧看看,也感谢相关作者提供的内容。
2、如果原作者认为侵权,请及时联系我,我的qq是244509154,邮箱是244509154@qq.com,我会及时删除侵权文章。
3、我的文章大家如果觉得对您有帮助或者您喜欢,请您在转载的时候请注明来源,不管是我的还是其他原作者,我希望这些有用的文章的作者能被大家记住。
4、最后希望大家多多的交流,提高自己,从而对社会和自己创造更大的价值。