从一系列整数中搜索

时间:2012-09-26 03:46:19

标签: c++ algorithm c++11

我需要查看整数列表中的整数。我对它们进行排序并使用lower_bound来查找给定整数所在的范围。这需要O(lgn)。有什么方法可以比这更好吗?

以下是要改进的提示。

  1. 给定列表总是正整数
  2. 列表是固定的。没有插入或删除。
  3. 一种方法是在数组中创建一个数组和索引。这可能不是空间有效的。 我可以使用unordered_map吗?我应该定义什么哈希函数?

    // Sort in reverse order to aid the lookup process
    vector<unsigned int> sortedByRange;
    //... sortedByRange.push_back(..)
    sort(sortedByRange.begin(), sortedByRange.end(), greater);
    Range = (sortedByAddress_.begin() - sortedByRange.end();
    std::cout<<"Range :"<<Range<<std::endl;    //prints 3330203948
    
    std::pair<unsigned int, unsigned int> lookup(unsigned int addr){
        pair<unsigned int, unsigned int> result;
        vector<unsigned int>::iterator it = lower_bound(sortedByRange.begin(), 
                                               sortedByRange.end(), addr);
        result.first = *it;
        result.second = *(it++);
        return result;
    }      
    

2 个答案:

答案 0 :(得分:1)

如果总范围不是很大,你可以构建一个任何方便大小的采样索引数组(你想要多少RAM?)

因此,例如,如果数据的总范围是256M,并且您有一个备用兆字节,则存储数据范围的每1K间隔的位置。然后,对于任何给定的数据点,您对索引数组执行O(1)(实际上是O(2):))探测以找到该数据点的最低和最高似是合理的范围,然后您可以在该数据点上执行lower_bound范围。如果你的范围在大小上没有大的变化,那么应该给你平均的恒定时间查找。

如果你不想在问题上留下那么多记忆,你可以根据平均范围大小和模糊因子尝试一对线性估计。如果事实证明不包含特定数据点,则可以回退到完整的二进制搜索;否则,限制范围内的二分搜索应该是平均线性时间。

这是第一个建议,如果手工操作不够清楚的话。完全未经测试的代码,甚至没有尝试编译它,并且整数类型的使用至少可以说是马虎。如果你使用它,试着让它更美丽。此外,我应该(但没有)将索引范围的开始限制为* begin_;如果它明显大于0,你应该修复它。

// The provided range must be sorted, and value_type must be arithmetic.
template<type RandomIterator, unsigned long size>
class IndexedLookup {
 public:
  using value_type = typename RandomIterator::value_type;
  IndexedLookup(RandomIterator begin, RandomIterator end)
    : begin_(begin),
      end_(end),
      delta_(*(end_ - 1) / size) {
    for (unsigned long i = 0; i < size; ++i)
      index_[i] = std::lower_bound(begin_, end_, i * delta_) - begin_;
      // The above expression cannot be out of range
    index_[size] = end_ - begin_;
  }

  RandomIterator lookup(value_type needle) {
    int low = needle / delta_;
    return std::lower_bound(index_[begin_ + low],
                            index_[begin_ + low + 1],
                            needle);
  }

 private:
  RandomIterator begin_, end_;
  value_type delta_;
  std::array<int, size + 1> index_;
}    

答案 1 :(得分:0)

方法1:如果您只需要知道列表中是否有给定的数字,并且最大值不是太大,您可以考虑使用位字段。查找将是O(1)操作。

方法2:如果值的范围很大(其中包含小整数和大整数),但列表大小不大(例如成千上万),您可以尝试(以编程方式)制作

的哈希函数
  1. 是列表中值的一对一;
  2. 会给出范围0 ... N + m的值m足够小;
  3. 计算起来相对便宜。
  4. 然后可以将常量列表的值放入由哈希值索引的数组中,以便快速检查给定输入值的包含。如果列表中存在漏洞(m非零),则应使用特殊值(例如-1)表示漏洞。

    包含测试:针对给定的输入   1.计算哈希值;   2.如果哈希值的值超出范围,则输入不在列表中;   3.当且仅当由哈希值索引的生成数组中的值与输入值相同时,输入才属于列表。

    如何制作哈希函数值得在SO中提出另一个问题(对于字符串值,存在用于为此目的生成工具的工具)。 : - )

    限制:如果列表不是在编译时创建的,而是在程序的运行时计算或接收,则此方法不适用。此外,如果此列表频繁更改,则生成散列函数和代码所需的计算时间可能会使此方法不适用。