面試前端必須準備的一個問題:怎麼去掉Javascript的Array的重複項。據我所知,百度、騰訊、盛大等都在面試裡出過這個題目。 這個問題看起來簡單,但其實是暗藏殺機。 考的不僅是實現這個功能,更能看出你對電腦程式執行的深入理解。
我總共想出了三種演算法來實現這個目的:
Array.prototype.unique1 = function() { var n = []; //一个新的临时数组 for(var i = 0; i < this.length; i++) //遍历当前数组 { //如果当前数组的第i已经保存进了临时数组,那么跳过, //否则把当前项push到临时数组里面 if (n.indexOf(this[i]) == -1) n.push(this[i]); } return n; } Array.prototype.unique2 = function() { var n = {},r=[]; //n为hash表,r为临时数组 for(var i = 0; i < this.length; i++) //遍历当前数组 { if (!n[this[i]]) //如果hash表中没有当前项 { n[this[i]] = true; //存入hash表 r.push(this[i]); //把当前数组的当前项push到临时数组里面 } } return r; } Array.prototype.unique3 = function() { var n = [this[0]]; //结果数组 for(var i = 1; i < this.length; i++) //从第二项开始遍历 { //如果当前数组的第i项在当前数组中第一次出现的位置不是i, //那么表示第i项是重复的,忽略掉。否则存入结果数组 if (this.indexOf(this[i]) == i) n.push(this[i]); } return n; }
其中第1種和第3種方法都用到了陣列的indexOf方法。此方法的目的是尋找存入參數在陣列中第一次出現的位置。很顯然,js引擎在實現這個方法的時候會遍歷數組直到找到目標。所以此函數會浪費掉很多時間。 而第2中方法用的是hash表。把已經出現過的透過下標的形式存入一個object內。下標的參考要比用indexOf搜尋數組快的多。
為了判斷這三種方法的效率如何,我做了一個測試程序,產生一個10000長度的隨機數組成的數組,然後分別用幾個方法來測試執行時間。 結果顯示第二種方法遠快於其他兩種方法。 但是記憶體佔用方面應該第二種方法比較多,因為多了一個hash表。這就是所謂的空間換時間。 就是這個測試頁面,你也可以去看看。
根據hpl大牛的思路,我寫了第四種方法:
Array.prototype.unique4 = function() { this.sort(); var re=[this[0]]; for(var i = 1; i < this.length; i++) { if( this[i] !== re[re.length-1]) { re.push(this[i]); } } return re; }
這個方法的想法是先將陣列排序,然後比較鄰近的兩個數值。 排序的時候用的JS原生的sort方法,JS引擎內部應該是用的快速排序吧。 最終測試的結果是此方法運行時間平均是第二種方法的三倍左右,不過比第一種和第三種方法快了不少。
第五種方法
最近在做【搜尋歷史記錄】功能也用到,開始用了 indexOf 方法,該方法在 ECMA5才有支持,對於 IE8- 就不支持了。
我們可以自己寫一個函數(Array物件的方法都是定義在原型物件上的),如下:
Array.prototype.unique = function(){ var length = this.length; if(length <= 1){ return this; } if(!Array.prototype.indexOf){ Array.prototype.indexOf = function(item){ var l = this.length, i = 0, r = -1; if(l <= 0){ return -1; } for(; i < l; i++){ if(this[i] === item){ r = i; } } return r; } } var result = []; //去重数组 for(var i = 0; i < length; i++){ if(result.indexOf(this[i]) === -1){ result.push(this[i]); } } return result; }
第六種方法
Array型別並沒有提供去重複的方法,如果要把陣列的重複元素幹掉,那得自己想辦法:
function unique(arr) { var result = [], isRepeated; for (var i = 0, len = arr.length; i < len; i++) { isRepeated = false; for (var j = 0, len = result.length; j < len; j++) { if (arr[i] == result[j]) { isRepeated = true; break; } } if (!isRepeated) { result.push(arr[i]); } } return result; }
整體思路是把數組元素逐一搬運到另一個數組,搬運的過程中檢查這個元素是否有重複,如果有就直接丟掉。從嵌套循環就可以看出,這種方法效率極低。我們可以 用一個hashtable的結構來記錄已有的元素,這樣就可以避免內層循環。