简体   繁体   English

JavaScript:如何使用二进制搜索在给定范围内的排序数组中查找数字

[英]JavaScript: how do I use binary search to find numbers in a sorted array within a given range

I am trying to write a function to return a subset of a sorted input array where all the numbers are within the input range.我正在尝试编写 function 以返回已排序输入数组的子集,其中所有数字都在输入范围内。 For example:例如:


getNumsWithin([1,2,3,5,6,7], [3.3, 6.7]) // [5, 6]
getNumsWithin([1,2,3,5,6,7], [6, 7]) // [6, 7]
getNumsWithin([1,2,3,5,6,7], [8, 10]) // []

Given the input array is always sorted, I assume I can use binary search to find a start index and an end index and just slice the original array with them.鉴于输入数组总是排序的,我假设我可以使用二进制搜索来找到开始索引和结束索引,然后用它们对原始数组进行切片。

Here is my attempt:这是我的尝试:

function findStartIdx(sortedArray, target) {
  let start = 0,
    end = sortedArray.length - 1

  while (start < end) {
    const mid = Math.floor((start + end) / 2)
    if (sortedArray[mid] < target) start = mid
    else end = mid - 1
  }

  return start + 1
}

function findEndIdx(sortedArray, target) {
  let start = 0,
    end = sortedArray.length - 1

  while (start < end) {
    const mid = Math.floor((start + end) / 2)
    if (sortedArray[mid] < target) start = mid + 1
    else end = mid
  }

  return end + 1
}

function getNumsWithin(array, range) {
  const startIndex = findStartIdx(array, range[0])
  const endIndex = findEndIdx(array, range[1])

  return array.slice(startIndex, endIndex + 1)
}

But the binary search would end up being a endless loop if the target happens to the last num in the array.但是,如果目标恰好是数组中的最后一个数字,则二分搜索最终将成为一个无限循环。 Also I wonder if we can just search it once instead of twice to find both the start and the另外我想知道我们是否可以只搜索一次而不是两次来找到起点和终点

I am struggling to write such a function. can someone help me out?我正在努力写这样的 function。有人可以帮我吗?

Your idea is fine, but the binary search functions need some changes:你的想法很好,但二进制搜索功能需要一些改变:

  • The while condition should allow start and end to be equal. while条件应该允许startend相等。
  • The comparison of target in the second version should be different so that equal values are included in the final range.第二个版本中target的比较应该不同,以便最终范围内包含相等的值。

Here is a correction of both:这是两者的更正:

function findStartIdx(sortedArray, target) {
  let start = 0,
    end = sortedArray.length - 1

  while (start <= end) {
    const mid = Math.floor((start + end) / 2)
    if (sortedArray[mid] < target) start = mid + 1
    else end = mid - 1
  }

  return start
}

function findEndIdx(sortedArray, target) {
  let start = 0,
    end = sortedArray.length - 1

  while (start <= end) {
    const mid = Math.floor((start + end) / 2)
    if (sortedArray[mid] <= target) start = mid + 1
    else end = mid - 1
  }

  return end
}

On a final note: in terms of time complexity it would be fine to only perform one binary search, and then continue with a linear search through the array.最后一点:就时间复杂度而言,只执行一次二分搜索,然后继续对数组进行线性搜索会很好。 This is because taking the slice represents the same time complexity as searching the end of the slice with a linear search: both are O(slicesize).这是因为获取切片代表与使用线性搜索搜索切片末尾相同的时间复杂度:两者都是 O(slicesize)。 But given that in JavaScript the slice method is fast compared to a more "manual" linear search with an explicit loop, you'll get better results with these two binary searches.但是考虑到在JavaScript中, slice方法比使用显式循环的更“手动”线性搜索更快,您将通过这两种二进制搜索获得更好的结果。

Just need to make sure findStartIdx works, then the rest is easy.只需要确保findStartIdx有效,然后 rest 就很容易了。 So instead of looking for exact match as in normal binary search , we look for a match between two numbers at index mid and mid + 1 .因此,我们不是像正常的二分查找那样寻找精确匹配,而是寻找索引为midmid + 1的两个数字之间的匹配。

Edge cases: haven't tested if same numbers appear multiple times.边缘情况:没有测试相同的数字是否出现多次。 Need to fix findEndIdx a bit.需要稍微修复findEndIdx

 var arr = [1, 2, 3, 5, 6, 7] var start = 0 var end = arr.length - 1; console.log(getNumsWithin(arr, [3.3, 6.7])) console.log(getNumsWithin(arr, [6, 7])) console.log(getNumsWithin(arr, [8, 10])) function findStartIdx(sortedArray, x, start, end) { if (start > end) return -1; let mid = Math.floor((start + end) / 2); if (arr[mid] <= x) { if (mid === arr.length - 1) { return mid; } if (arr[mid + 1] >= x) return mid; return findStartIdx(sortedArray, x, mid + 1, end); } else { return findStartIdx(sortedArray, x, start, mid - 1); } } // hehe function findEndIdx(sortedArray, x, start, end) { var result = findStartIdx(sortedArray, x, start, end) + 1 if (result == sortedArray.length) return -1; return result; } function getNumsWithin(array, range) { const startIndex = findStartIdx(array, range[0], start, end) const endIndex = findEndIdx(array, range[1], start, end) if (startIndex == -1 || endIndex == -1) { return []; } return array.slice(startIndex, endIndex + 1) }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM