【问题标题】:Why does BinarySearch use ~ operator to return a negative number?为什么 BinarySearch 使用 ~ 运算符返回负数?
【发布时间】:2021-04-20 20:21:16
【问题描述】:

this code 中,有一个二分搜索算法。为了表示未找到该项目,返回一个负数,在代码中使用return ~mid 完成(参见line 92)。

public int Search(string name)
{
    int low = 0;
    int high = _sortedPositions.Count - 1;
    int mid = 0;
    while (low <= high)
    {
        mid = (low + high) >> 1;
        _stream.Position = _sortedPositions[mid];
        var curName = _br.ReadString();
        var comp = string.Compare(name, curName);
        if (comp == 0)
            return mid;
        if (comp < 0)
            high = mid - 1;
        else
            low = mid + 1;
    }
    return ~mid;
}

我的问题是:为什么会这样?返回一个固定的负整数有什么好处,比如return -1

【问题讨论】:

  • 如果您在特定问题上需要帮助,请在您的问题中发布所有相关代码;不是代码链接。
  • “与返回一个固定的负整数相比有什么优势吗?” - 这是一种方便的方式来报告所需元素不存在,同时还提供索引它会在哪里。参见例如docs.microsoft.com/en-us/dotnet/api/….
  • 我正要回答你的问题,但它已关闭。答案是肯定的,有优势。如果您有记录 所在位置的按位补码,您可以使用 ~ 撤消按位补码并找到您正在搜索的记录的插入索引,以防您需要添加您正在搜索的列表中的一条记录。
  • .NET 做同样的事情github.com/dotnet/corert/blob/…
  • 请注意,这个函数有一个错误,所以它在很多情况下返回不正确的负值。有关详细信息,请参阅我的答案。

标签: c# bitwise-operators binary-search


【解决方案1】:

是的,有一个好处(如果函数返回正确的值,但它没有,请参阅下文)

负数表示在集合中没有找到所搜索的键,就像返回 -1 一样

但是,与简单地返回 -1 相比,您获得的好处是,您可以再次翻转这个负值的位,以返回值 应该是的索引(如果它是)遵循排序顺序。

你可以这样使用它:

int index = Search("Some name");
if (index < 0)
    // insert at position (~index)

现在,这个特定的搜索函数有一个缺陷,它会在找到最后一个要比较的项目的边界情况下错误地报告错误的索引,但它搜索的名称应该是 after em> 这个项目。

例如,尝试在搜索函数报告的位置中添加以下两个字符串:A,然后是Z

它将以错误的顺序添加它们,ZA 之前。

这样做的原因是它找到了应该在索引 0 处比较的单个项目 A,但应该在索引 0 之后添加 Z,而不是 at 索引 0。

但是,这个错误的修复很简单:

  1. mid 的初始计算移出循环
  2. ... 并在更改 lowhigh 后在循环内进行重新计算

这样最终的mid 值将是正确的。这是最终的功能:

public int Search(string name)
{
    int low = 0;
    int high = _sortedPositions.Count - 1;
    int mid = low + (high - low) / 2;                  // note #2
    while (low <= high)
    {
        var curName = _sortedPositions[mid];           // note #1
        var comp = string.Compare(name, curName);
        if (comp == 0)
            return mid;
        if (comp < 0)
            high = mid - 1;
        else
            low = mid + 1;
        mid = low + (high - low) / 2;                  // note #2
    }
    return ~mid;
}

(注意 #1:我将 sortedPositions 切换为简单的 List&lt;string&gt;,同时我正在研究您的代码以使其运行。显然这对于​​您基于流的示例将有所不同。)

(注意 #2:我计算 mid 值的方法是通过计算差值并除以 2,这是建议的方法。如果集合有很多值,low + high 将溢出,建议的方式不会。)

许多实现采用的一个更简单的修复是,在您没有更多项目可查看的地方,low == mid(具有上述更改),因此对该错误的不同修复将是返回 @987654339 @ 而不是~mid,然后必须执行最小的更改:

public int Search(string name)
{
    int low = 0;
    int high = _sortedPositions.Count - 1;
    int mid;
    while (low <= high)
    {
        mid = low + (high - low) / 2;                  // note #2
        var curName = _sortedPositions[mid];           // note #1
        var comp = string.Compare(name, curName);
        if (comp == 0)
            return mid;
        if (comp < 0)
            high = mid - 1;
        else
            low = mid + 1;
    }
    return ~low;
}

【讨论】:

  • 不仅修复 #2 避免了错误,而且使代码更清晰恕我直言。顺便说一句,我想亲眼验证修复,这里有一些小提琴:dotnetfiddle.net/lu4BTk
  • 确实,return ~lo 是 .NET 代码的作用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-23
  • 2021-04-28
  • 2016-05-21
  • 2018-03-30
相关资源
最近更新 更多