JavaScript 二分查找 & KMP 算法

KMP 查找

Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)可在一个主文本字符串 str1 内查找一个词 str2 的出现位置。

思路

生成 next 数组

next 数组是决定了 KMP 算法的下一步匹配的位置信息。

1 根据需查找字符串 str2 生成一段 next 数组信息
2 next 数组长度与 str2 长度一致
3 next 数组每一项表达的意思是 str2 当前位置最大匹配前缀与最大匹配后缀的长度信息

最大前缀、最大后缀匹配长度

str2 当前位置前缀后缀的最大匹配字符串长度
考察[aaaaa]{i} i 前的字符
前缀不包括最后一个字符,后缀不包括第一个字符

var str2 = 'abcabcdabc'
var next = [-1, 0, 0, 0, 1, 2, 3, 0, 1, 2]
// 0 位置前方无匹配为 -1, 1 位置前方匹配只有一个字符串为 0 (人为规定)
// 4 位置 str2 为 b,前方最大前缀和后缀只有 [a]bc[a]{b} 中的 a 所以为1
// 5 位置 str2 为 c,前方最大前缀和后缀匹配 [ab]c[ab]{c} 中的 ab 长度为2
// 6 位置 str2 为 d,前方最大前缀和后缀匹配 [abc][abc]{d} 中的 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 // 人为规定 next 第0位 -1
  next[1] = 0 // 人为规定 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]) // 匹配上,继续匹配下一位,next 数组当前位置就是 cn 位置前的字符长度,就是 cn 值, 0-6,cn为7,就是7
      next[i++] = ++cn
    else if (cn > 0) // 没匹配上,cn 跳到前数组匹配位上,也就是当前 next 数组 cn 位置最大前后缀长度的位置,
      cn = next[cn]
    else // cn 跳到 0 位置 当前的 next[cn] === 0 
      next[i++]  = 0 // 那么 next[i] 位置就为 0 无匹配字符
  }
  return next
}

使用 next 数组信息加速查找

1 根据 str2 获取到 next 数组
2 str1 和 str2 从第一位字符开始比较
3 当 str1[i1] === str2[i2] 匹配上,i1++, i2++ 按顺序匹配
4 当 next[i2] === -1,str2 与 str1 的第0位没匹配上,str2 的第0位继续跟 str1 下1位比较

var str1 = 'abcdef'
var str2 = 'bcd']
// str2 第0位 b 没匹配上 str1 第0位 a,从下一位比较

5 匹配过程中没有匹配上,则 str2 从当前 next[i2] 处进行推移,考察 str2 最大前缀下一位和 str1 i1 位置,而 str2 最大前缀下一位就是 next 数组当前 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) // 根据 str2 生成长度一样的最大匹配前缀后缀的 next 数组

  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 // 人为规定 next 第0位 -1
  next[1] = 0 // 人为规定 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
}

二分查找

在已排序数组查找某数

思路

1 待查找数是 n
2 考察已排序数组 mid 中间位置数是否是 n
3 如果不是,比较 n 与 arr[mid] 大小
4 如果 n > arr[mid] 则从 mid 右边继续查找 新的 arr[mid] 中间位置, 否则就从左边开始查找
5 直到考察到 n === arr[mid] 返回 mid 位置。

代码

/*
 * @params {Array} arr1 已排序数组
 * @params {Array} arr2 
 * @return {Array} arr2 中不存在于 arr1 的数
 */
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
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容