从一系列整数中搜索

Search from a range of integers

本文关键字:搜索 整数 一系列      更新时间:2023-10-16

我需要从整数列表中查找一个整数。我对它们进行排序,并使用lower_bound来找到给定整数的范围。这需要O(lgn)。有什么办法比这更好吗?

以下是需要改进的提示。

  1. 给定列表总是正整数
  2. 列表是固定的。没有插入或删除

一种方法是创建一个数组并索引到该数组。这可能不节省空间。我可以使用无序映射吗?我应该定义什么散列函数?

// 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;
}      

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

因此,例如,如果数据的总范围是256M,并且您有一个备用兆字节,那么您将存储数据范围中每1K间隔的位置。然后,对于任何给定的数据点,您对索引数组进行O(1)(实际上是O(2):))探测,以找到该数据点的最低和最高合理范围,然后您可以仅对该范围进行lowest_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:如果您只需要知道给定的数字是否在列表中,并且最大值不太大,可以考虑使用位字段。那么查找将是O(1)运算。

方法2:如果值的范围很大(其中有小整数和大整数),但列表大小不大(例如几千),您可以尝试(通过编程)制作一个

  1. 对列表中的值是一对一的
  2. 将给出范围为0。。。CCD_ 2和CCD_
  3. 计算起来相对便宜

然后可以将常量列表的值放入一个数组中,并根据哈希值进行索引,以便快速检查给定输入值的包含情况。如果列表中有孔(m非零),则应使用特殊值(例如-1)指示这些孔。

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

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

限制:如果列表不是在编译时创建的,而是在程序运行时计算或接收的,则此方法不适用。此外,如果这个列表经常更改,那么生成哈希函数和代码所需的计算时间可能会使这种方法不合适。

Javascript

let searchRangeInterger = function(nums, target) {
let res = [-1, -1];
let leftSide = find(nums, target, true);
let rightSide = find(nums, target, false);
if (!nums.length) return res;
if (leftSide > rightSide) return res;
return [leftSide, rightSide];
};
let find = function (nums, target, findLeft) {
var left = 0;
var right = nums.length - 1;
var mid = 0;
while (left <= right) {
mid = Math.floor((left + right) / 2);
if (nums[mid] > target || (findLeft && nums[mid] === target)) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return findLeft ? left : right;
};