137. 只出現一次的數字 II(劍指offer 56-II)

知識點:雜湊表;位運算

題目描述

給你一個整數陣列 nums ,除某個元素僅出現 一次 外,其餘每個元素都恰出現 三次 。請你找出並返回那個只出現了一次的元素。

你的演算法應該具有線性時間複雜度。 你可以不使用額外空間來實現嗎?

示例
輸入:nums = [2,2,3,2]
輸出:3 輸入:nums = [0,1,0,1,0,1,99]
輸出:99

解法一:HashMap

和136題一樣,使用雜湊表儲存每個數字出現的數字,再遍歷雜湊表,看誰能等於1;

很容易想到,但這樣做的壞處就是需要開闢新的空間;

class Solution {
public int singleNumber(int[] nums) {
Map<Integer,Integer> map = new HashMap<>();
for(Integer i : nums){
map.put(i, map.getOrDefault(i, 0)+1);
}
for(Integer i: map.keySet()){
if(map.get(i) == 1) return i;
}
return -1;
}
}

時間複雜度:O(N);

空間複雜度:O(N);

解法二:位運算

這題不能再用異或去解了,因為變成了三個沒有辦法抵消,但是想象一下,如果有三個一模一樣的數字,那這三個數字二進位制相加後,所有位上要麼是0;要麼全是3的倍數;然後我們的多餘元素,要麼加上去為0;要麼加上去多了一個1,所以可以依次求每位的和,然後%3,如果值為1,那證明我們在這位上的值為1;否則為0;

如下圖所示;

class Solution {
public int singleNumber(int[] nums) {
//在java中int型別是32位,我們需要統計所有數字在某一位置的和能不能被3整除,
// 如果不能被3整除,說明那個只出現一次的數字的二進位制在那個位置是1……把32位全部統計完為止
int ans = 0;
for(int i = 0; i < 32; i++){
int count = 0; //統計1的個數;
for(int j = 0; j < nums.length; j++){
count += (nums[j] >> i) & 1; //統計所有數在第i位上1的個數;
}
if(count % 3 != 0){
ans = ans | (1 << i); //其他位不變,第i位置為1;
}
}
return ans;
}
}

時間複雜度:O(N);

空間複雜度:O(1);

體會

**掌握位運算的解決方法:這種題目往往要按位與、按位異或等操作;

此外還會有左移<<;右移>>等;比如說:

a & 1 : a的其他位全為0,最後一位不變:即取a最後一位;

a | (1 << i) : a的其他位不變,把a的第i位置為1;

(a >> i) & 1 : 取出a第i位上的值;