在排序的数组中搜索,几乎没有比较

时间:2015-11-26 10:22:51

标签: c++ algorithm search sorted

您将获得std::vector<T>个不同的项目。已经排序了。 类型T仅支持少于 <运算符进行比较。这是一个很重要的功能。所以你必须尽可能少地使用它。

有没有比二分查找更好的解决方案? 如果没有,有没有比这更好的解决方案,使用少于运营商的次数?

template<typename T>
int FindKey(const std::vector<T>& list, const T& key)
{
    if( list.empty() )
        return -1;

    int left = 0;
    int right = list.size() - 1;
    int mid;

    while( left < right )
    {
        mid = (right + left) / 2;
        if( list[mid] < key )
            left = mid + 1;
        else
            right = mid;
    }

    if( !(key < list[left]) && !(list[left] < key) )
        return left;    

    return -1;
}

这不是一个现实世界的情况,只是一个编码测试。

3 个答案:

答案 0 :(得分:1)

您可以使用hash table(例如unordered_map来折算额外的 O(n)预处理时间,以便分摊 O(1)查询时间3}})创建lookup table

散列表计算密钥的hash functions,不要比较密钥本身。

两个键可能具有相同的哈希值,导致冲突,这解释了为什么不保证每个单独的操作都是恒定时间。 Amortized常量时间意味着如果执行总计花费时间 t k 操作,则商 t / k = O(1) ,对于足够大的 k

Live example

#include <vector>
#include <unordered_map>

template<typename T>
class lookup {
  std::unordered_map<T, int> position;
public:
  lookup(const std::vector<T>& a) {
    for(int i = 0; i < a.size(); ++i) position.emplace(a[i], i);
  }
  int operator()(const T& key) const {
    auto pos = position.find(key);
    return pos == position.end() ? -1 : pos->second;
  }
};

这也需要额外的内存。

如果值可以映射到整数并且在a reasonable range范围内(即 max-min = O(n)),则只需使用vector作为查找表而不是unordered_map。具有保证常量查询时间的好处。

有关更详细的讨论,请参阅此answer to "C++ get index of element of array by value",包括线性,二进制和哈希索引查找的经验比较。

更新

如果T类型的接口不支持除bool operator<(L, R)之外的其他操作,那么使用decision tree model可以证明lower bound for comparison-based search algorithms为Ω(log n)。< / p>

答案 1 :(得分:0)

您可以使用std::lower_bound。它通过log(n)+1比较来实现,这是您问题的最佳复杂性。

template<typename T>
int FindKey(const std::vector<T>& list, const T& key)
{
  if(list.empty())
    return -1;
  typename std::vector<T>::const_iterator lb =
        std::lower_bound(list.begin(), list.end(), key);
  // now lb is an iterator to the first element
  // which is greater or equal to key
  if(key < *lb)
    return -1;
  else
    return std::distance(list.begin(), lb);
}

通过额外检查相等性,您可以使用log(n)+2比较。

答案 2 :(得分:0)

如果您的号码是正态分布的,您可以在日志日志中使用插值搜索。如果他们有其他分发,您可以修改它以考虑您的分发,但我不知道哪些分发产生日志日志时间。

https://en.wikipedia.org/wiki/Interpolation_search