【问题标题】:Finding range index of a sorted array on a specific element在特定元素上查找排序数组的范围索引
【发布时间】:2016-09-26 21:42:38
【问题描述】:

我正在使用对集合进行排序的数组。任务是找到一个值的范围。

假设我们有这个排序数组:

int[] array = {1,1,1,3,3,9,10}

示例 我们必须找到元素 1 的范围最小值和最大值。我们很快发现元素 1 的第一个索引是 0,范围最大值是 2。

我们现在知道 0 到 2 之间的范围都有值 1。如果搜索的元素是 3,我们看到范围是 3-4,而元素 9 的范围是 6-6。

我已经使用线性搜索来执行此操作,但如果有更快的方法来执行此操作,我会听到吗?

【问题讨论】:

    标签: arrays algorithm


    【解决方案1】:

    您可以使用两种版本的二分搜索来查找最左边和最右边的位置:

    int[] array = { 1, 1, 1, 3, 3, 9, 10 }
    int value = ...
    
    int leftmost(int min, int max)
    {
        if (min == max) return min
        int mid = (min + max) / 2
    
        if (array[mid] < value) return leftmost(mid + 1, max)
        else return leftmost(min, mid)
    }
    
    int rightmost(int min, int max)
    {
        if (min == max) return min
        int mid = (min + max + 1) / 2
    
        if (array[mid] > value) return rightmost(min, mid - 1)
        else return rightmost(mid, max)
    }
    

    只需使用min=0max=array.length-1 拨打电话即可。时间复杂度为O(logn)

    你会得到这样的输出(0-based 索引):

    value=1  -> left=0, right=2
    value=3  -> left=3, right=4
    value=9  -> left=5, right=5
    value=10 -> left=6, right=6
    

    【讨论】:

    • 时间复杂度将是 logn 而不是 nlog(n)
    • 我不建议完全使用这个公式,因为如果搜索值不在数组中,您仍然会返回一些有效的索引。最好使用传统的if (min &gt; max) return -1 提前退出。然后你必须想出一个新的检查来确定成功......
    【解决方案2】:

    二分查找会快得多。您可以对此here

    有所了解

    【讨论】:

    • 二分查找的问题是它只返回一个索引。例如。如果我们搜索元素 1,它将返回 1 作为索引。但我正在寻找一个范围。
    【解决方案3】:

    假设一个更大的数组先是二进制然后是线性的。

    根据数组的大小,更快的方法可能是线性的。 如果您使用 7 个数字的数组重复进行此搜索,那么您不会看到太大的性能差异。实际上它可能会更慢。

    但是,尽管扩展该数组以便拥有更多数据,并且最好先进行二分搜索,然后再进行线性搜索。

    执行一次并抓取范围的底部,然后进行线性以找到范围的终点。

    你可以为终点做另一个二进制,但随着时间的推移,它应该平均到一个简单的线性函数的洗涤。

    【讨论】:

      【解决方案4】:

      虽然@Arturo Menchaca 的答案是正确的,但我认为递归解决方案可能有点难以理解。因此,对于那些喜欢迭代方法的人...

      public int[] FindRange(int[] arr, int value)
      {
          int[] upperLowerBounds = new int[2];
          int startIndex = 0;
          int endIndex = arr.Length - 1;
          while (startIndex < endIndex)
          {
              int midIndex = (startIndex + endIndex) / 2;
              if (value <= arr[midIndex])
              {
                  endIndex = midIndex;
              }
              else
              {
                  startIndex = midIndex + 1;
              }
          }
      
          // saving startIndex in the result array that will be returned...
          upperLowerBounds[0] = startIndex;
          endIndex = arr.Length - 1;
          while (startIndex < endIndex)
          {
              int midIndex = (startIndex + endIndex) / 2 + 1;
              if (value < arr[midIndex])
              {
                  endIndex = midIndex  -1;
              }
              else
              {
                  startIndex = midIndex;
              }
          }
      
          upperLowerBounds[1] = endIndex;
          return upperLowerBounds;
      }
      

      和单元测试..

      [TestCase(new int[] { -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, 1, 6, 8, 10, 10, 10, 10, 10, 10, 25 }, -3, 1, 9)] // search element = -3
      [TestCase(new int[] { -4, -3, -1, 1, 6, 8, 10, 10, 10, 10, 10, 10, 25 }, 6, 4, 4)] // search element = 6
      [TestCase(new int[] { -4, -3, -1, 1, 6, 8, 10, 10, 10, 10, 10, 10, 25 }, 10, 6, 11)] // search element = 10
      [TestCase(new int[] { -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, -1, -1, -1, -1, -1, -1, -1, 1, 6, 8, 10, 10, 10, 10, 10, 10, 25 }, -1, 10, 17)] // search element = -1
      [TestCase(new int[] { 1, 1, 1, 3, 3, 9, 10 }, 3, 3, 4)] // search element  = 3
      [TestCase(new int[] { 1, 1, 1, 3, 3, 9, 10 }, 1, 0, 2)] // search element = 1
      public void FindRangeTest(int[] arr, int value, int expectedStartIndex, int expectedEndIndex)
      {
          int[] range = runner.FindRange(arr, value);
      
          // Assert.
          Assert.AreEqual(expectedStartIndex, range[0]);
          Assert.AreEqual(expectedEndIndex, range[1]);
      }
      

      我用过 C# 和 nunit 3.10

      【讨论】:

        猜你喜欢
        • 2020-11-17
        • 2012-12-01
        • 1970-01-01
        • 2014-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多