【WEB前端】JavaScript陣列去重
前幾日在做js-assessment時,發現其陣列這一章裡也有陣列去重的這一問題:這個問題說起來十分簡單,就是把陣列中重複的元素去除。其實個人感覺陣列去重問題實際上就是排序的升級版,目前開來最好的去重方法就是字典去重,這一點和排序中的基數排序不謀而合。下面就簡單的說一說自己解決這個問題的思路。
編寫AOP時間函式
對於解決陣列去重演算法的好壞,最終效率是第一位的,所以需要編寫一個計算函式執行時間的切面函式。實現如下:
Function.prototype.time = function() {
var t1 = +new Date()
, foo = this()
, t2 = +new Date()
return t2 - t1 //返回單位為毫秒
}
但是寫完這個方法之後發現,對於要測試執行的函式而言,在進行測試之前不能夠執行(即只能寫成 foo.time()
的樣子),這樣就不能用普通傳參的方法對其進行引數傳遞。突然想到了在前幾日看到過prototypejs中的原始碼中有一個 bind
函式,其功能就在與給一個函式繫結特定上下文,且返回函式本身而不立即執行,於是就馬上實現了這樣一個函式,程式碼如下:
Function.prototype.bind = function(ob) {
var fn = this
, slice = Array.prototype.slice
, args = slice.call(arguments, 1)
return function(){
return fn.apply(ob, args.concat(slice.apply(arguments)))
}
}
寫完這兩個,我們就可以對測試函式進行執行時間計算,假如陣列為 arr
,測試函式為 delrep
,則在實際操作中可以這樣實現:delrep.bind(arr).time()
(執行函式的同時輸出運算時間)。
雙重迴圈去重
在就去重方法討論的文章中,愚人碼頭的文章裡說到過這個方法,當然,作者本身也承認,這種雙重for迴圈巢狀的方法在大資料量的情況下十分耗時。作者的原始碼引用如下:
Array.prototype.delRepeat=function(){
var newArray=new Array();
var len=this.length;
for (var i=0;i<len ;i++){
for(var j=i+1;j<len;j++){
if(this[i]===this[j]){
j=++i;
}
}
newArray.push(this[i]);
}
return newArray;
}
這裡我也用ECMAScript中宣告的 forEach
方法和 indexOf
方法模擬實現一下雙重迴圈:
function delrep1() {
var n = []
this.forEach(function(v) {
if (n.indexOf(v) == -1)
n.push(v)
})
return n
}
作者的程式碼看起來像極了氣泡排序:每一次操作都會冒出一個沒有重複元素的,放入新的陣列(對應氣泡排序冒出最小的)。而氣泡排序的時間複雜度是O(n^2),可以想見這個演算法的效率著實不高。而在我的演算法中採用了 indexOf
的方法,沒想這個遍歷的效率要高很多,最終執行的時間要比作者的方法高不少。這裡貼一下最終執行的時間(用隨機生成的15w長度的陣列進行測試,編譯器使用的是想向大家極力推薦的nodejs,雖然這裡只用了它一個小小的功能):
malcolm@malcolm:~/test/aop$ node aop.js
method0: 1389ms #我的方法
method1: 9087ms #作者的方法
各中原理,還需要仔細的分析才行。
字典去重
之後作者提了一個字典去重的方法,我用自己的方法簡化了一下:
function delrep2() {
var n = {}
, r = []
this.forEach(function(v){
if (!n[v]) {
n[v] = true
r.push(v)
}
})
return r
},
這個一看就很臉熟——傳說中時間複雜度只有O(n)的基數排序麼!類似與撲克牌的發牌,一次遍歷什麼的不是最快捷了麼。當然這裡用到了“空間換時間”的策略,多出來一個龐大的字典,但是為了效率,做一點犧牲也是必要的。執行的時間也令人歎為觀止:
malcolm@malcolm:~/test/aop$ node aop.js
method0: 1389ms
method1: 9087ms
method2: 9ms
但是令人遺憾的是,這個方法是有bug的:你把所有的元素都轉化成字典的鍵值key,也就是字串,那必然會出現1和'1'的問題。在陣列中他們並不是重複元素,而這裡只能保留一個。這可怎麼辦呢?在愚人碼頭帖子的回覆中,馬上有人提到了,既然鍵值轉化為字串後失去了型別,那如果在轉化之前給他加上型別會怎麼樣呢?程式碼如下:
function delrep3() {
var n = {}
, r = []
this.forEach(function(v){
if (!n[typeof(v) + v]) {
n[typeof(v) + v] = true
r.push(v)
}
})
return r
},
不過作者在回覆裡說,這個方法的效率和兩重迴圈的差不多,難道真的是這樣麼?實際測試了一下:
malcolm@malcolm:~/test/aop$ node aop.js
method0: 1389ms
method1: 9087ms
method2: 9ms
method3: 56ms
明顯已經好了不少啊~可是可以看出因為 typeof
的原因,效率比第二個方法低了不少,但是沒有bug的優勢足以彌補這一缺憾。
排序遍歷
問題說到這裡好像已經解決了,但是實際上背後的演算法問題我自己還是沒有搞清楚,希望以後能把詳細的原因補上。這裡也寫一個我自己最初想到的方法:要去重先遍歷,依靠javascript自身的sort函式先幫我們過一關。據說這個函式用的是O(nlgn)的快速排序,顯然是已經比冒泡要好不少了。排完序之後重複的資料都疊在一起,排頭向棧裡壓資料,遇到與棧頂相同的元素則不壓。我的演算法如下:
function delrep4() {
var n = []
this.sort()
n.push(this[0])
this.forEach(function(v) {
if (v !== n[0])
n.unshift(v)
})
return n
}
最終的時間還是比較可觀的,不過還是沒有方法2的改進版本好,資料如下:
malcolm@malcolm:~/test/aop$ node aop.js
method0: 1389ms
method1: 9087ms
method2: 9ms
method3: 56ms
method4: 88ms
看起來還不錯~
在完成測試的同時參考了這篇文章:JS陣列去重問題,文中還提到了用排序之後用splice刪除重複元素的方法,不得不承認這樣確實節省了一部分空間,但是js的splice方法,要涉及刪除後的陣列整列移動,道爺可是在蝴蝶書中點名說本方法“大型陣列中效率較低”的。自己在測試的時候發現在15w資料量的時候用splice還是效率還是可以,再多的話就明顯不如我自己的方法了。不過綜合來說,如果可以保證陣列內部是純數值,用字典排序絕對還是最明智的選擇。
希望下週可以補上對各個演算法時間複雜度的簡要說明。
相關文章
- web前端陣列處理之陣列去重Web前端陣列
- JavaScript陣列去重JavaScript陣列
- JavaScript 陣列去重JavaScript陣列
- 好程式設計師web前端之JavaScript陣列去重方法程式設計師Web前端JavaScript陣列
- javascript之陣列去重JavaScript陣列
- JavaScript陣列去重方法JavaScript陣列
- 也談JavaScript陣列去重JavaScript陣列
- JavaScript陣列去重方法總結JavaScript陣列
- JavaScript專題之陣列去重JavaScript陣列
- JS陣列去重 – JSON陣列去重陣列JSON
- javascript陣列去重程式碼例項JavaScript陣列
- javascript陣列去重(ES6版)JavaScript陣列
- 陣列去重陣列
- 《前端演算法系列》陣列去重前端演算法陣列
- JavaScript陣列去重(12種方法,史上最全)JavaScript陣列
- 從 JavaScript 陣列去重談效能優化JavaScript陣列優化
- 陣列物件去重陣列物件
- js陣列去重JS陣列
- JavaScript實現陣列去重的常見方式JavaScript陣列
- 解鎖多種JavaScript陣列去重姿勢JavaScript陣列
- 關於陣列去重陣列
- 陣列去重和求和陣列
- 陣列去重的方法陣列
- 陣列包含字典 去重陣列
- 陣列扁平和去重陣列
- js物件陣列去重JS物件陣列
- js陣列物件去重JS陣列物件
- golang 陣列去重 移除陣列指定元素Golang陣列
- 陣列去重和陣列扁平化陣列
- 前端筆試題——陣列去重(保姆級手撕)前端筆試陣列
- JavaScript&ES6----陣列去重的四種方法JavaScript陣列
- 陣列去重,地址不改變陣列
- 陣列的去重和排序陣列排序
- JS實現陣列去重JS陣列
- js 陣列去重小技巧JS陣列
- 如何實現陣列去重?陣列
- 陣列求和,刪除,去重陣列
- iOS 模型陣列去重複iOS模型陣列