演算法-計算小陣列在大陣列中的索引
前言
大陣列:[1,2,3,4,5,6]
小陣列:[4,5]
我們需要快速得到小陣列在大陣列中的索引位置,本例中結果是3
演算法思路
1. 暴力,巢狀for迴圈
function match(arr1,arr2){ var n = arr1.length var m = arr2.length f1: for(var i=0;i<n;i++){ if(arr1[i]===arr2[0]){ f2: for(var j=1;j<m;j++){ if(arr1[i+j]!==arr2[j])continue f1; } return i } } return -1 }
2. 將陣列元素轉成字串,用特殊字元分割
如
[1,11,3,4,5,6].join('#')="1#11#3#4#5#6" [4,5].join('#')="4#5"
然後利用字串匹配演算法就可以算得,根據匹配字元前面的#
的在源串中是第幾個#
來得到
問題:
- 引入了分割字元後,搜尋長度變大,演算法耗時變高
- 當陣列中元素也含有字串時,就需要辨別1和"1"
- 當陣列中元素含有字串時,所選的特殊字元不能處於字串的字符集中
問題2 我們可以採用JSON格式,就可以識別數字和字串等基本型別了。
譬如:JSON.stringify([1,'1','3'])
得到[1,"1","3"]
,我們去除左右兩側中括號得到1,"1","3"
在遍歷的時候,我們需要識別分割符,
和陣列元素為字串且裡面還有,
的情況,這樣就可以解決問題3。但是這樣將限定很多字串匹配演算法,同時還引入問題4
問題 4:大陣列中某個元素為字串,且內容為小陣列格式化的結果,導致查詢錯誤
即 大陣列:[1,'1,"1","3"','3'],小陣列:[1,"1","3"],正常應該不能匹配,但這裡按字串匹配演算法的話會導致匹配。
因此還是需要用字符集中沒出現的字元做分隔符。需要一個O(n)的預處理時間,此外問題1還是不能解決。
PS:最後演算法複雜度為O(n),那問題1就影響不大了。
3. 借鑑 Rabin-Karp 演算法思想
具體演算法可以參考ofollow,noindex" target="_blank">《字串匹配演算法介紹及js字串indexOf原始碼探究》
我們需要把小陣列中的元素對映到某個數值,為了快速計算,我們定義以下規則:
number型別:取值=原值%100,將值控制在0~99
之間
string型別:取值=第一個字元的ascii碼*字串長度%100+100,將值控制在100~199
之間
故字符集長度M為200,我們設雜湊表長度Q為 10007
我們寫出以下程式碼:
var Q = 10007 var M = 200 function getValue(item){ if(typeof item === "number"){ return item%100 } else if(typeof item === "string"){ return item.length>0?item.charCodeAt(0)*item.length%100+100:100 } else return 0 } function getHash(arr,len){ var val = 0 for(var i=0;i<len;i++){ val=(val*M+getValue(arr[i]))%Q } return val } //逐個比較相同長度的陣列,arr1從index位置開始取 function compare(arr1,arr2,offset){ for(var i=0;i<arr2.length;i++){ if(arr1[i+offset]!==arr2[i])return false } return true } function match(arr1,arr2){ var n = arr1.length var m = arr2.length if(n<m)return -1 var arr2Hash = getHash(arr2,m) // 一個固定值 var fix = Q-M**(m-1)%Q var curHash = getHash(arr1,m) if(curHash===arr2Hash&&compare(arr1,arr2,0))return 0 for(var i=m;i<arr1.length;i++){ var offset = i-m+1 curHash = (((curHash+getValue(arr1[i-m])*fix%Q)*M)+getValue(arr1[i]))%Q if(curHash===arr2Hash&&compare(arr1,arr2,offset))return offset } return -1 } //match([1,2,3,'1',2,'3'],['1',2]) = 3
實際測試,該演算法效率並不高,在於getValue
需要做的操作太多了