剑指 Offer 03. 数组中重复的数字

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、最后希望大家多多的交流,提高自己,从而对社会和自己创造更大的价值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值