只出现一次(N次)的数字 / 出现次数最多的数字 / 数组中数字出现的次数

一.题目类型简介

        数组中数字出现的次数是一类经典的问题,通常让我们求数组中数字出现的次数及其衍生的问题,比如,只出现一次的数字,只出现两次的数字,在一个数组中只有一个数字出现一次,其他出现两次或者三次,数组中出现次数最多,数组中出现的次数超过半数等。

二.经常使用的方法

        在这类题目中,我们可以使用暴力循环、HashMap、排序、异或运算等来解决,但是在面试等场景下,此类题目通常要求O(N)的时间复杂度和O(1)空间复杂度或者不使用额外空间,即比暴力解在时间和空间上要更优的方法。

三.典型例题

1.leetcode 136 只出现一次的数字

给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

输入:nums = [2,2,1]
输出:1

输入:nums = [4,1,2,1,2]
输出:4

输入:nums = [1]
输出:1

排序

class Solution {
    public int singleNumber(int[] nums) {
        Arrays.sort(nums);
        for(int i = 0; i < nums.length-1; i += 2){
            if(nums[i] != nums[i+1]){
                return nums[i];
            }
        }
        return nums[nums.length-1];
    }
}

HashMap

class Solution {
    public int singleNumber(int[] nums) {
        HashMap map = new HashMap<>();
        for(int i = 0; i < nums.length; i++){
            if(map.containsKey(nums[i])){
                map.put(nums[i],map.get(nums[i])+1);
            }
            else{
                map.put(nums[i],1);
            }
        }
        for(Map.Entry entry : map.entrySet()){
            if(entry.getValue() == 1){
                return entry.getKey();
            }    
	    }
        return nums[0];
    }
}

HashSet优化 Ref.[1][2]

public static int singleNumber_HashSet(int[] nums) {

    int len = nums.length;
    Set set = new HashSet<>();

    for (int i = 0; i < len; i++) {
        // 尝试将当前元素加入 set
        if (!set.add(nums[i])) {
            // 当前元已经存在于 set,即当前元素第二次出现,从 set 删除
            set.remove(nums[i]);
        }
    }

    // 最后只剩一个不重复的元素
    return set.iterator().next();
}

异或运算

class Solution {
    public int singleNumber(int[] nums) {
        int ans = 0;
        for(int i : nums){
            ans ^= i;
        }
        return ans;
    }
}

异或基本运算

和零运算:A\oplus 0 = A

本身运算:A\oplus A = 0

交换律和结合律:A\oplus B\oplus C = A\oplus C\oplus B = \left ( A\oplus B \right ) \oplus C

"必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。"按照题目的要求,只有此方法才满足,排序的时间是O(NlogN),而HashMap和HashSet的优化是O(N),但是空间上不满足要求,开辟了Hash空间,空间复杂度是O(N)的,只有异或的方法,或者说,本质上就希望我们使用异或的方法。

当在一个数组中只有一个数字出现了一次,我们不妨假设数组已经是排好序的,那么对于排好序的数组来说,有着一对一对的数,比如1和1,2和2,9和9,我们发现,1\oplus 1=0,其他一对相同的数字也是如此,最后只剩下了0和一个单个的数字,而A\oplus 0 = A

2.剑指 Offer II 004 只出现一次的数字 

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

输入:nums = [2,2,3,2]
输出:3

输入:nums = [0,1,0,1,0,1,100]
输出:100

排序

class Solution {
    public int singleNumber(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        for (int i = 0; i < n - 2; i += 3) {
            if (nums[i + 1] != nums[i]) return nums[i];
        }
        return nums[n - 1]; 
    }
}

HashMap

class Solution {
    public int singleNumber(int[] nums) {
        HashMap map = new HashMap<>();
        for(int i = 0; i < nums.length; i++){
            if(map.containsKey(nums[i])){
                map.put(nums[i],map.get(nums[i])+1);
            }
            else{
                map.put(nums[i],1);
            }
        }
        for(Map.Entry entry : map.entrySet()){
            if(entry.getValue() == 1){
                return entry.getKey();
            }    
	    }
        return nums[0];
    }
}

数位运算{Ref.3}

class Solution {
    public int singleNumber(int[] nums) {
        int ret = 0;
        for (int i = 0; i < 32; i++) {
            int cnt = 0;
            for (int num : nums) {
                cnt += num >> i & 1;
            }
            if (cnt % 3 != 0) {
                ret |= 1 << i;
            }
        }
        return ret;
    }
}

只出现一次(N次)的数字 / 出现次数最多的数字 / 数组中数字出现的次数_第1张图片

异或{Ref.4}

class Solution {
    public int singleNumber(int[] nums) {
        int one = 0, two = 0;
        for(int x : nums){
            one = one ^ x & ~two;
            two = two ^ x & ~one;
        }
        return one;
    }
}

本种方法思想牵扯到有限状态自动机,更多详情可参考[4]。

参考来源Ref.

[1] leetcode 神奇小超 通过哈希集、按位异或操作符解决(解题思路)

[2] leetcode ageovb 通过哈希集、按位异或操作符解决(解题思路)

[3] leetcode 清风Python 刷穿剑指offer-Day02-整数II 004.只出现一次的数字 位运算讲解 

[4] leetcode Krahets 剑指 Offer II 004. 只出现一次的数字(有限状态自动机 + 位运算,清晰图解)

你可能感兴趣的:(leetcode,数据结构与算法,Java,leetcode,算法,数据结构)