JavaScript 二分查找 & KMP 算法

KMP 查找

Knuth-Morris-Pratt字符串查找算法(簡稱為KMP算法)可在一個主文本字符串 str1 內(nèi)查找一個詞 str2 的出現(xiàn)位置慕嚷。

思路

生成 next 數(shù)組

next 數(shù)組是決定了 KMP 算法的下一步匹配的位置信息驮肉。

1 根據(jù)需查找字符串 str2 生成一段 next 數(shù)組信息
2 next 數(shù)組長度與 str2 長度一致
3 next 數(shù)組每一項表達的意思是 str2 當前位置最大匹配前綴與最大匹配后綴的長度信息

最大前綴挫望、最大后綴匹配長度

str2 當前位置前綴后綴的最大匹配字符串長度
考察[aaaaa]{i} i 前的字符
前綴不包括最后一個字符潦刃,后綴不包括第一個字符

var str2 = 'abcabcdabc'
var next = [-1, 0, 0, 0, 1, 2, 3, 0, 1, 2]
// 0 位置前方無匹配為 -1, 1 位置前方匹配只有一個字符串為 0 (人為規(guī)定)
// 4 位置 str2 為 b缘缚,前方最大前綴和后綴只有 [a]bc[a]虽惭 中的 a 所以為1
// 5 位置 str2 為 c,前方最大前綴和后綴匹配 [ab]c[ab]{c} 中的 ab 長度為2
// 6 位置 str2 為 d接谨,前方最大前綴和后綴匹配 [abc][abc]kydstt7 中的 abc 長度為3
// 7 位置 str2 為 a杭攻,前方最大前綴和后綴匹配 abcabcd{a} 中的無前后綴匹配值,長度為 0

示例

代碼

function getNextArray(strArr) {
  if (strArr.length === 1) return Array.of(-1) // [-1]
  var next/*Number[]*/ = new Array(strArr.length)

  next[0] = -1 // 人為規(guī)定 next 第0位 -1
  next[1] = 0 // 人為規(guī)定 next 第1位 0
  var i = 2 // 初始位置為 2疤坝,因為 2 位置前才有 2 個字符可比較
  var cn = 0 // cn 初始匹配為 0, [cn,i-1]{i}
  while (i < next.length) {
    if (strArr[i - 1] === strArr[cn]) // 匹配上兆解,繼續(xù)匹配下一位,next 數(shù)組當前位置就是 cn 位置前的字符長度跑揉,就是 cn 值, 0-6,cn為7锅睛,就是7
      next[i++] = ++cn
    else if (cn > 0) // 沒匹配上埠巨,cn 跳到前數(shù)組匹配位上,也就是當前 next 數(shù)組 cn 位置最大前后綴長度的位置现拒,
      cn = next[cn]
    else // cn 跳到 0 位置 當前的 next[cn] === 0 
      next[i++]  = 0 // 那么 next[i] 位置就為 0 無匹配字符
  }
  return next
}

使用 next 數(shù)組信息加速查找

1 根據(jù) str2 獲取到 next 數(shù)組
2 str1 和 str2 從第一位字符開始比較
3 當 str1[i1] === str2[i2] 匹配上辣垒,i1++, i2++ 按順序匹配
4 當 next[i2] === -1,str2 與 str1 的第0位沒匹配上印蔬,str2 的第0位繼續(xù)跟 str1 下1位比較

var str1 = 'abcdef'
var str2 = 'bcd']
// str2 第0位 b 沒匹配上 str1 第0位 a勋桶,從下一位比較

5 匹配過程中沒有匹配上,則 str2 從當前 next[i2] 處進行推移侥猬,考察 str2 最大前綴下一位和 str1 i1 位置例驹,而 str2 最大前綴下一位就是 next 數(shù)組當前 i2 的長度值


6 至到 i1 或者 i2 字符撞線,i2 撞線則匹配出位置退唠,i1 撞線則無匹配

代碼

function getIndexOf(s1, s2) {
  var str1 = s1.spilit('')
  var str2 = s2.split('')
  var i1 = 0
  var i2 = 0
  var next = getNextArray(str2) // 根據(jù) str2 生成長度一樣的最大匹配前綴后綴的 next 數(shù)組

  while (i1 < str1.length && i2 < str2.length) {
    if (str1[i1] === str2[i2])
      i1++, i2++
    else if (next[i2] === -1)
      i1++
    else
      i2 = next[i2]
  }
  return i2 === str2.length ? i1 - i2 : -1
}

function getNextArray(strArr) {
  if (strArr.length === 1) return Array.of(-1) // [-1]
  var next/*Number[]*/ = new Array(strArr.length)

  next[0] = -1 // 人為規(guī)定 next 第0位 -1
  next[1] = 0 // 人為規(guī)定 next 第1位 0
  var i = 2
  var cn = 0
  while (i < next.length) {
    if (strArr[i - 1] === strArr[cn])
      next[i++] = ++cn
    else if (cn > 0)
      cn = next[cn]
    else
      next[i++]  = 0
  }
  return next
}

二分查找

在已排序數(shù)組查找某數(shù)

思路

1 待查找數(shù)是 n
2 考察已排序數(shù)組 mid 中間位置數(shù)是否是 n
3 如果不是鹃锈,比較 n 與 arr[mid] 大小
4 如果 n > arr[mid] 則從 mid 右邊繼續(xù)查找 新的 arr[mid] 中間位置, 否則就從左邊開始查找
5 直到考察到 n === arr[mid] 返回 mid 位置。

代碼

/*
 * @params {Array} arr1 已排序數(shù)組
 * @params {Array} arr2 
 * @return {Array} arr2 中不存在于 arr1 的數(shù)
 */
function getNotIncluded(arr1, arr2) {
  var res = []
  for (var i = 0; i < arr2.length; i++) {
    var l = 0;
    var r = arr1.length - 1
    var has = false
    while (l <= r) { // 二分查找核心
      var mid = l + ((r - l) >> 1)
      if (arr1[mid] === arr2[i]) {
        has = true // 找到則跳出
        break
      }
      if (arr1[mid] > arr2[i])
        r = mid - 1
      if (arr1[mid] < arr2[i])
        l = mid + 1
    }
    if (!has)
      res.push(arr2[i])
  }
  return res
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瞧预,一起剝皮案震驚了整個濱河市屎债,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌垢油,老刑警劉巖盆驹,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異滩愁,居然都是意外死亡躯喇,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門惊楼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玖瘸,“玉大人秸讹,你說我怎么就攤上這事檀咙。” “怎么了璃诀?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵弧可,是天一觀的道長。 經(jīng)常有香客問我劣欢,道長棕诵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任凿将,我火速辦了婚禮校套,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘牧抵。我一直安慰自己笛匙,他們只是感情好侨把,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著妹孙,像睡著了一般秋柄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蠢正,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天骇笔,我揣著相機與錄音,去河邊找鬼嚣崭。 笑死笨触,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的有鹿。 我是一名探鬼主播旭旭,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼葱跋!你這毒婦竟也來了持寄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤娱俺,失蹤者是張志新(化名)和其女友劉穎稍味,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荠卷,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡模庐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了油宜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掂碱。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖慎冤,靈堂內(nèi)的尸體忽然破棺而出疼燥,到底是詐尸還是另有隱情,我是刑警寧澤蚁堤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布醉者,位于F島的核電站,受9級特大地震影響披诗,放射性物質(zhì)發(fā)生泄漏撬即。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一呈队、第九天 我趴在偏房一處隱蔽的房頂上張望剥槐。 院中可真熱鬧,春花似錦宪摧、人聲如沸粒竖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽温圆。三九已至挨摸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間岁歉,已是汗流浹背得运。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锅移,地道東北人熔掺。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像非剃,于是被迫代替她去往敵國和親置逻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容