遇到一道面試題:找到數組中第一個非重複的數。面試
[ 1, 1, 2, 2, 3, 4, 4, 5 ]
第一個非重複的數爲 3
最簡單的想法就是兩層 for
循環遍歷數組,這樣的時間複雜度是 O(n^2)
。而更高效的方式,是使用hash Map
,可將時間複雜降爲O(n)
。數組
其實這個題目能夠衍生出三個相似的問題:數據結構
我準備用ES6
中的 Map
數據結構來解決這三個問題,在這以前有必要先梳理下Map的主要知識點。函數
JavaScript 的對象(Object),本質上是鍵值對的集合(Hash 結構),可是傳統上只能用字符串看成鍵。這給它的使用帶來了很大的限制。爲了解決這個問題,ES6 提供了 Map
數據結構。它相似於對象,也是鍵值對的集合,可是「鍵」的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵。也就是說,Object
結構提供了「字符串—值」的對應,Map
結構提供了「值—值」的對應,是一種更完善的 Hash 結構實現。code
例如Map構造函數接受一個數組做爲其參數:對象
const map = new Map([ [1, '張三'], [2, '李四'] ]); // 0:{1 => "張三"} // 1:{2 => "李四"}
Map
實例的屬性和操做方法:ip
size
:返回成員總數set(key, value)
:添加新的鍵值get(key)
:讀取鍵對應的值has(key)
:是否有某個鍵delete(key)
:刪除某個鍵clear()
:清空Map
實例的遍歷方法:字符串
keys()
:返回鍵名的遍歷器。values()
:返回鍵值的遍歷器。entries()
:返回鍵值對的遍歷器。forEach()
:遍歷 Map 的全部成員。下面來經過代碼解決三個問題:get
去重前:[ 1, 1, 2, 2, 3, 4, 4, 5 ]
去重後:[ 1, 2, 3, 4, 5 ]
主要思路:建立一個空Map
,遍歷原始數組,把數組的每個元素做爲key
存到Map中,由於Map
中不會出現相同的key
值,因此最終獲得的Map
中的全部key
值就是去重後的結果。hash
function arrayNonRepeatfy(arr) { let hashMap = new Map(); let result = new Array(); // 數組用於返回結果 for (let i = 0; i < arr.length; i++) { if(hashMap.has(arr[i])) { // 判斷 hashMap 中是否已有該 key 值 hashMap.set(arr[i], true); // 後面的true 表明該 key 值在原始數組中重複了,false反之 } else { // 若是 hashMap 中沒有該 key 值,添加 hashMap.set(arr[i], false); result.push(arr[i]); } } return result; } let arr = [1, 1, 1, 2, 3, 3, 4, 5, 5, "a", "b", "a"]; console.log(arrayNonRepeatfy(arr)); // [ 1, 2, 3, 4, 5, 'a', 'b' ]
上面最終產生的Map
不只能夠達到去重的效果,並且對每一元素的重複性都作了標註,這樣想找到找到數組中重複的數就很方便了:
console.log(hashMap); /* 0:{1 => true} {key: 1, value: true} 1:{2 => false} {key: 2, value: false} 2:{3 => true} {key: 3, value: true} 3:{4 => false} {key: 4, value: false} 4:{5 => true} {key: 5, value: true} 5:{"a" => true} {key: "a", value: true} 6:{"b" => false} {key: "b", value: false} */
[ 1, 1, 2, 2, 3, 4, 4, 5 ]
[ 1, 2, 4 ]
接上一節末尾,既然hashMap
中記錄了每個元素的重複狀況,找到重複的數就很簡單了,遍歷最終獲得的hashMap
,值爲true
對應的鍵就是重複的數:
function findRepeatNumInArray(arr) { let hashMap = new Map(); let result = new Array(); for (let i = 0; i < arr.length; i++) { hashMap.set(arr[i], hashMap.has(arr[i])) } // 獲得 hashMap 後,對其進行遍歷,值爲 true,對應的鍵就是重複的數 for(let [key, value] of hashMap.entries()) { if(value === true) { result.push(key); } } return result; } let arr = [1, 1, 1, 2, 3, 3, 4, 5, 5, "a", "b", "a"]; console.log(findRepeatNumInArray(arr));
[ 1, 1, 2, 2, 3, 4, 4, 5 ]
3
代碼與上一節的差很少,遍歷最終獲得的hashMap
,第一個值爲false
對應的鍵就是第一個非重複數字:
function findFirstNonRepeat(arr) { let hashMap = new Map(); for (let i = 0; i < arr.length; i++) { hashMap.set(arr[i], hashMap.has(arr[i])) } // 找到第一個值爲 false 的,就表明第一個非重複數,return 就行了 for(let [key, value] of hashMap.entries()) { if(value === false) { return key; } } return "所有重複"; } let arr = [1, 1, 1, 2, 3, 3, 4, 5, 5, "a", "b", "a"]; console.log(findFirstNonRepeat(arr));
總結,三類問題的核心其實就是:利用 Map
存儲每個數字的重複狀況。