【问题标题】:Binary Search with an unknown number of items具有未知数量的项目的二分搜索
【发布时间】:2011-04-08 05:44:35
【问题描述】:

假设您不知道要搜索的元素数量,并且给定一个 API,该 API 接受索引并且如果您超出范围(如此处使用 getWordFromDictionary 方法实现)将返回 null,您如何执行二进制为客户端程序搜索并实现 isWordInDictionary() 方法?

此解决方案有效,但我最终在找到初始高索引值的级别之上进行了串行搜索。对较低值范围的搜索受到this answer 的启发。我还查看了 Reflector(C# 反编译器)中的 BinarySearch,但它的列表长度是已知的,因此仍在寻找填补空白。

private static string[] dictionary;

static void Main(string[] args)
{
    dictionary = System.IO.File.ReadAllLines(@"C:\tmp\dictionary.txt");

    Console.WriteLine(isWordInDictionary("aardvark", 0));
    Console.WriteLine(isWordInDictionary("bee", 0));
    Console.WriteLine(isWordInDictionary("zebra", 0));
    Console.WriteLine(isWordInDictionaryBinary("aardvark"));
    Console.WriteLine(isWordInDictionaryBinary("bee"));
    Console.WriteLine(isWordInDictionaryBinary("zebra"));
    Console.ReadLine();
}

static bool isWordInDictionaryBinary(string word)
{
    // assume the size of the dictionary is unknown

    // quick check for empty dictionary
    string w = getWordFromDictionary(0);
    if (w == null)
        return false;

    // assume that the length is very big.
    int low = 0;
    int hi = int.MaxValue;

    while (low <= hi)
    {
        int mid = (low + ((hi - low) >> 1));
        w = getWordFromDictionary(mid);

        // If the middle element m you select at each step is outside 
        // the array bounds (you need a way to tell this), then limit
        // the search to those elements with indexes small than m.
        if (w == null)
        {
            hi = mid;
            continue;
        }

        int compare = String.Compare(w, word);
        if (compare == 0)
            return true;

        if (compare < 0)
            low = mid + 1;
        else
            hi = mid - 1;
    }

    // punting on the search above the current value of hi 
    // to the (still unknown) upper limit
    return isWordInDictionary(word, hi);
}


// serial search, works good for small number of items
static bool isWordInDictionary(string word, int startIndex) 
{
    // assume the size of the dictionary is unknown
    int i = startIndex;
    while (getWordFromDictionary(i) != null)
    {
        if (getWordFromDictionary(i).Equals(word, StringComparison.OrdinalIgnoreCase))
            return true;
        i++;
    }

    return false;
}

private static string getWordFromDictionary(int index)
{
    try
    {
        return dictionary[index];
    }
    catch (IndexOutOfRangeException)
    {
        return null;
    }
}

答案后的最终代码

static bool isWordInDictionaryBinary(string word)
{
    // assume the size of the dictionary is unknown

    // quick check for empty dictionary
    string w = getWordFromDictionary(0);
    if (w == null)
        return false;

    // assume that the number of elements is very big
    int low = 0;
    int hi = int.MaxValue;

    while (low <= hi)
    {
        int mid = (low + ((hi - low) >> 1));
        w = getWordFromDictionary(mid);

        // treat null the same as finding a string that comes 
        // after the string you are looking for
        if (w == null)
        {
            hi = mid - 1;
            continue;
        }

        int compare = String.Compare(w, word);
        if (compare == 0)
            return true;

        if (compare < 0)
            low = mid + 1;
        else
            hi = mid - 1;
    }

    return false;
}

【问题讨论】:

    标签: arrays algorithm binary-search


    【解决方案1】:

    您可以分两个阶段实现二进制搜索。在第一阶段,您增加了您正在搜索的区间的大小。一旦您检测到您超出了界限,您就可以在您找到的最新区间中进行正常的二进制搜索。像这样的:

    bool isPresentPhase1(string word)
    {
      int l = 0, d = 1;
      while( true ) // you should eventually reach an index out of bounds
      {
        w = getWord(l + d);
        if( w == null )
          return isPresentPhase2(word, l, l + d - 1);
        int c = String.Compare(w, word);
        if( c == 0 )
          return true;
        else if( c < 0 )
          isPresentPhase2(value, l, l + d - 1);
        else
        {
          l = d + 1;
          d *= 2;
        } 
      }
    }
    
    bool isPresentPhase2(string word, int lo, int hi)
    {
        // normal binary search in the interval [lo, hi]
    }
    

    【讨论】:

    • 感谢您为此编写代码。我接受了@Sasha 的建议,因为它更适合我在问题中的代码。
    【解决方案2】:

    当然可以。从索引一开始,将查询索引加倍,直到遇到比查询词更大的词法(编辑:或 null)。然后你可以再次缩小搜索空间,直到找到索引,或者返回 false。

    编辑:请注意,这不会添加到您的渐近运行时,它仍然是 O(logN),其中 N 是系列中的项目数。

    【讨论】:

      【解决方案3】:

      所以,我不确定我是否完全理解您的描述中的问题,但我假设您正在尝试搜索一个长度未知的 sorted 数组以查找特定字符串。我还假设实际数组中没有空值;如果您要求的索引超出范围,则该数组仅返回 null。

      如果这些都是真的,那么解决方案应该只是一个标准的二分搜索,尽管你在整个整数空间中进行搜索,并且你只是将 null 视为查找在你正在寻找的字符串之后的字符串.基本上只是想象你的 N 个字符串的排序数组实际上是一个 INT_MAX 字符串的排序数组,最后以空值排序。

      我不太明白的是,您似乎基本上已经这样做了(至少粗略地看一下代码),所以我想我可能无法完全理解您的问题。

      【讨论】:

      • 你的假设是完全正确的。有意思,我试试看。我的代码试图找到一个返回数据的初始 hi 值,但也许它不需要,正如你所建议的,只是将 null 视为大于。
      • 确实,将 null 视为我们正在搜索的单词之后的单词是对我的代码的最简单修复。谢谢!
      猜你喜欢
      • 2013-09-25
      • 2015-04-12
      • 2019-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-01
      • 1970-01-01
      相关资源
      最近更新 更多