題目地址:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/
題目描述
一個整型數組 nums
里除兩個數字之外,其他數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。要求時間復雜度是O(n),空間復雜度是O(1)。
題目示例
示例 1:
輸入:nums = [4,1,4,6]
輸出:[1,6] 或 [6,1]
示例 2:
輸入:nums = [1,2,10,4,1,4,3,3]
輸出:[2,10] 或 [10,2]
解題思路
哈希表:初看這道題目,發現又是計數問題,對於計數問題,哈希表是一種很方便的方法,在這里我們使用哈希表arr計數nums數組中的每個元素出現的次數,對出現一次的元素,我們將它存入到數組res中,最后返回res即可,不過空間復雜度不滿足O(1)要求。
位運算(異或):首先要明確異或的含義,異或顧名思義就是兩者相同,則異或結果為0,否則結果為1,簡言之,n^n=0,n^m=1,需要注意的是任何數與0異或結果為它本身,即n^0=n。回歸到本題,出來2個數出現了一次之外,其余數均出現了兩次。分析題目,如果除了一個數出現一次外,其余數字出現的次數均出現的次數為2次,那么這個元素如何求呢?不難發現,只要我們遍歷數組全員異或一下即可得到,這是因為異或運算具有交換率,兩兩相同的元素異或結果必然為0,這樣最后就只剩下出現一次的那個元素了。對於兩個出現一次的元素求解,我們可以采用二進制的方法將所有元素分成兩組,使得兩個只出現一次的元素在不同的組中,以及相同的元素被分配到相同的組中,然后對兩個組分別進行異或運算,即可得到兩個只出現一次的元素。其中,我們假設兩個只出現一次的元素分別為a和b,那么所有元素異或的結果就是a和b的異或結果。
程序源碼
哈希表
class Solution { public: vector<int> singleNumbers(vector<int>& nums) { if(nums.size() == 0) return {}; unordered_map<int, int> arr; vector<int> res; for(int i = 0; i < nums.size(); i++) { arr[nums[i]] += 1; } for(int i = 0; i < nums.size(); i++) { if(arr[nums[i]] == 1) res.push_back(nums[i]); } return res; } };
異或運算
class Solution { public: vector<int> singleNumbers(vector<int>& nums) { if(nums.size() == 0) return {}; int number = 0; //異或結果 vector<int> res(2, 0); for(int i = 0; i < nums.size(); i++) { number ^= nums[i]; //全員異或得到兩個只出現一次元素的異或結果number,其中number的二進制值至少包含一個1,否則,結果就是0,表明兩元素相同,與題意不符 } int pos = number & (-number); //按位與&,找到number最低位起第一個1的位置,,其中,-number表示number的相反數,即取反加1,例pos=(010&(110))=010,而與運算只有當兩者均為1是結果才為1,否則為0,即0 & 0 = 0, 0 & 1 = 0, 1 & 0 = 0, 1 & 1 = 1。 for(int i = 0; i < nums.size(); i++) //以pos為標准分組,這個位置是1的數字,放到第一個數組里做異或運算,不是1的放到第二組。 { if((nums[i] & pos) == pos) res[0] ^= nums[i]; //這個位置是1的時候,放入第一個數組做異或運算 else res[1] ^= nums[i]; } return res; } };
參考文章