前言
這題目的邏輯是找出陣列中只出現過一次的元素,直覺是用一層 for 迴圈遍歷整個陣列後,使用 HashMap 來儲存元素跟出現的次數,最後再遍歷 Map 出結果,不過這樣同時會用到兩個 for 迴圈,時間複雜度推估會達到 O(N^2)
,另外討論區有比較高階的算法只需用到一個迴圈,時間複雜度可達 O(n)
,這裡有 JAVA 和 Python 的寫法。
題目
Given a non-empty array of integers nums , every element appears twice except for one. Find that single one. You must implement a solution with a linear runtime complexity and use only constant extra space.
給定一個非空的 整數陣列叫 nums ,每一個元素都會出現兩次除了一個以外。找到那個單獨的 整數。 你必須實現一種方案是線性的運作時間,而且只能使用常數的記憶體空間。
題目連結:https://leetcode.com/problems/single-number/
題目限制
1 <= nums.length <= 3 * 104
3 * 104 <= nums[i] <= 3 * 104
Each element in the array appears twice except for one element which appears only once.
範例
1 2 Input: nums = [2 ,2 ,1 ] Output: 1
1 2 Input: nums = [4 ,1 ,2 ,1 ,2 ] Output: 4
1 2 Input: nums = [1 ] Output: 1
思路&筆記
這邊第一個直覺是用迴圈把所有的整數全部取出來後,依依放在 Map 裡當 Key,然後把每個整數所出現的次數記錄在 Map 裡當 Value,最後遍歷這個 Map 取出 Value 小於 2 的 Key,因為小於 2 就代表陣列中只出現過一次,這個就是我們要的整數了!
int num: nums
遍歷陣列,把出現的元素記錄到 Map
判斷有無這個元素
沒有元素:放進 Map,元素為 Key、整數為 Value
有元素:當前的元素的整數 Value +1
遍歷 Map 判斷裡面的 Key 的 Value < 2
就回傳 Key
JAVA 初階實現
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { public int singleNumber (int [] nums) { int result = 0 ; Map<Integer, Integer> isMap = new HashMap <Integer, Integer>(); for (int num: nums){ if (!isMap.containsKey(num)) { isMap.put(num, 1 ); }else { isMap.put(num, isMap.get(num)+1 ); } } for (Integer key: isMap.keySet()){ if (isMap.get(key) < 2 ){ return result = key; } } return result; } }
JAVA 進階實現
筆者還在學習中,參考了討論區裡網友的解法,這個技巧比較特別,有點偏向數學題目。
這裡會使用到邏輯運算 XOR,位元運算的特性,數學的交換律。
了解以下說明,步驟會很明瞭,運用數學交換律來排序後,新的排序會從小排到大而且一樣的整數會倆倆相鄰,然後已知的兩整數用邏輯運算後相不相同表示 0 或 1,得出最後無法被削除的整數是 4。
1 2 3 4 5 6 7 8 9 邏輯運算 XOR 將倆正整數都轉成 binay 後再進行比較,那雙方的 binay 是一樣的話為 0 ,不一樣的話為 1 。(覺得複雜難理解的話可看下方算式) 每個位元會分開算 int 4 => binay 00000000 00000000 00000000 00000100 int 1 => binay 00000000 00000000 00000000 00000001 --------------------------------------------------------- 00000000 00000000 00000000 00000101 => 5 所以 4 ^ 1 = 5
1 2 3 4 5 6 7 8 9 10 11 12 位元運算的特性 A^A => if A = 0 , 0 ^0 => 0 if A = 1 , 1 ^1 => 0 A^0 => if A = 0 , 0 ^0 => 0 if A = 1 , 1 ^0 => 1 A^0 => A (得出1 也就是A) A^B => B^A (交換律意思是相加時可以互相顛倒: A+B => B+A) 最終得到一個現象,自己跟自己做=0 ,任何人跟0 做=任何人,A跟B做=B跟A做
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 如何使用在題目 int [] nums = {2 , 2 , 1 } xor = 2 ^2 ^1 說明: 1 ) 2 ^2 自己跟自己做 => 0 (任何自己跟自己做都是等於0 )2 ) 0 ^1 0 再跟1 做 => 1 ^0 做 (注意這裡不是得到A^0 )3 ) 1 ^0 1 跟0 做 => 1 結果答案得出 xor = 1 int [] nums = {4 , 1 , 2 , 1 , 2 }做排序 (因為交換律的特性所以都是相等的,是數學原理排序的不是電腦排的) 4 ^1 ^2 ^1 ^2 =>1 ^4 ^2 ^1 ^2 =>1 ^4 ^1 ^2 ^2 =>1 ^1 ^4 ^2 ^2 =>1 ^1 ^2 ^4 ^2 =>1 ^1 ^2 ^2 ^4 (最終)題目規定,每個整數會出現2 次,排序完後整數會倆倆相鄰,所以算出來一樣的會變0 1 ^1 => 0 2 ^2 => 0 最後會變成 0 ^0 ^4 0 的部分全部都會消掉,所以最後答案剩下 4
1 2 3 4 5 6 7 8 9 10 11 12 class Solution { public int singleNumber (int [] nums) { int xor = 0 ; for (int i : nums) { xor ^= i; } return xor; } }
Python 實現
使用了和 Java 一樣的邏輯執行,換成 Python 的寫法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution : def singleNumber (self, nums: [int ], result: int ) -> int : dicList = {} for i in nums: if i not in dicList: dicList[i]=1 else : dicList[i] = dicList[i]+1 for i in dicList: if dicList[i] == 1 : return i return
成績
Language
Runtime
Memory
Java
14ms
43.6MB
Python
163ms
19.4MB
參考資料
https://www.youtube.com/watch?v=Hy1hE0HBR3U