最长公共前缀(二分查找)

360 阅读1分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一、题目描述:

查找字符串数组中的最长公共前缀

输入: strs = ["flower","flow","flight"]
输出: "fl"

二、思路分析:

我们要计算公共前缀,可以用第一个字符串作为参数物,挨个和其他字符串进行比较。当发现第一个字符串的某个字符和其他字符不相等时,说明这个位置就是分界线了。

function longestCommonPrefix(strs) {
  if (strs == null || strs.length == 0) {
    return ''
  }
  for (let i = 0; i < strs[0].length; i++) {
    let c = strs[0][i]
    for (let j = 1; j < strs.length; j++) {
      if (i === strs[j].length || strs[j][i] !== c) {
        return strs[0].substring(0, i)
      }
    }
  }
  return strs[0]
}

该算法的时间复杂度:O(mn)

除了使用挨个字符扫描的形式,我们还可以使用二分查找的方法。

function longestCommonPrefix(strs) {
  if (strs == null || strs.length == 0) {
    return ''
  }
  // 最短字符串的长度
  let minLength = Number.MAX_VALUE
  for (let i = 0; i < strs.length; i++) {
    minLength = Math.min(minLength, strs[i].length)
  }
  // 二分查找
  let low = 0;
  let high = minLength
  while (low < high) {
    let mid = (high + low) >> 1
    if (isCommonPrefix(strs, mid)) {
      low = mid + 1
    } else {
      high = mid - 1
    }
  }
  return strs[0].substring(0, low)
}

function isCommonPrefix(strs, length) {
  let str0 = strs[0].substring(0, length)
  let count = strs.length
  for (let i = 1; i < count; i++) {
    let str = strs[i]
    for (let j = 0; j < length; j++) {
      if (str0[j] !== str[j]) {
        return false
      }
    }
  }
  return true
}

最长公共字符会在0到最短字符串长度之间[0, minLength],所以我们可以根据[0, minLength]做二分查找。时间复杂度:O(mnlogm)

每次取查找范围的中间值mid,判断每个字符串的长度为mid的前缀是否相同,如果相同则最长公共前缀的长度一定大于或等于mid,如果不相同则最长公共前缀的长度一定小于 mid

四、总结:

二分查找是面试中最容易出现的题目,所以大家一定要提前掌握。二分查找法虽然简单,但写好它并没有那么容易,比如不能很好的判断边界条件,可能出现错误的结果或者出现死循环。


var searchInsert = function (arr, x) {
  let left = 0;
  let right = arr.length - 1;
  while (left <= right) {
    let mid = (left + right) >> 1
    if (arr[mid] === x) { // x=a[n/2]
      return mid
    }else if (x < arr[mid]) { // x<a[n/2]
      right = mid - 1
    } else { // x>a[n/2]
      left = mid + 1
    }
  }
  return -1
}

上面代码用来查找一个数字在有序数组中的索引位置。