从整数范围内搜索
Posted
技术标签:
【中文标题】从整数范围内搜索【英文标题】:Search from a range of integers 【发布时间】:2012-09-26 03:46:18 【问题描述】:我需要从整数列表中查找一个整数。我对它们进行排序并使用 lower_bound 来查找给定整数的范围。这需要 O(lgn)。有没有比这更好的方法?
以下是改进的提示。
-
给定的列表总是正整数
列表已修复。没有插入或删除。
一种方法是创建一个数组并索引到数组中。这可能不节省空间。 我可以使用 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;
【问题讨论】:
没有代码 - 没有答案 - 先尝试一下 @AdrianCornish:这更多是算法问题。我认为 OP 已经提到了 lower_bound。 我不在乎 - 显示代码或没有答案,这看起来很像我看到的 100,000 个作业问题,您希望其他人为您做作业。试过什么代码,计时结果如何…… 我可以使用 unordered_map 吗?当然。我应该定义什么哈希函数?什么工作最快取决于输入和 # 桶 - 例如,如果它们是有效的随机数或递增数字,您可以直接使用它们。如果# buckets 是素数,并且这些数字并非全部从素数的倍数病理上偏移,那么您也可以使用这些数字。如果你想要一些通用的东西,然后谷歌搜索一个强大的哈希函数。如果您需要通常足够好,请使用 boost::hash 或让 unordered_map 使用其默认值。 @nhahtdh 然后显示你尝试过的代码,作业与否,你的实际结果是什么 - 先尝试一下 - 不要要求为你完成工作 【参考方案1】:如果总范围不是很大,您可以构建一个任何方便大小的采样索引数组(您想投入多少 RAM?)
因此,例如,如果数据的总范围是 256M,并且您有一个空闲的兆字节,那么您存储数据范围的每 1K 间隔的位置。然后对于任何给定的数据点,你做一个 O(1) (实际上是 O(2) :) )探测索引数组以找到该数据点的最低和最高合理范围,然后你可以做最低限度范围。如果您的范围在大小上没有太大变化,那应该会给您平均恒定时间查找。
如果您不想在问题上投入太多内存,可以尝试基于平均范围大小和模糊因子的一对线性估计。如果结果不包含特定数据点,则可以回退到完整的二进制搜索;否则,同样,限制范围内的二进制搜索应该是平均线性时间。
这是第一个建议,以防挥手不够清晰。完全未经测试的代码,甚至没有尝试编译它,并且至少可以说整数类型的使用是草率的。如果你使用它,试着让它更漂亮。我也应该(但没有)将索引范围的开始限制为 *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_;
【讨论】:
【参考方案2】:方法一:如果你只需要知道给定的数字是否在列表中,并且最大值不是太大,你可以考虑使用位域。查找将是 O(1) 操作。
方法 2: 如果值的范围很大(其中有小整数和大整数),但列表大小不大(例如几千),您可以尝试(以编程方式)制作一个散列函数,
-
在列表中的值上是一对一的;
将给出一个范围为
0
... N + m
的值,而m
足够小;
计算起来相对便宜。
然后可以将常量列表的值放入由哈希值索引的数组中,以便快速检查给定输入值的包含情况。如果列表中有空洞(m
非零),则应使用特殊值(例如-1
)指示空洞。
包含测试:对于给定的输入 1.计算哈希值; 2.如果哈希值的值超出范围,则输入不在列表中; 3. 否则,当且仅当由哈希值索引的生成数组中的值与输入值相同时,输入才属于列表。
如何制作散列函数值得在 SO 中提出另一个问题(对于字符串值,存在为此目的生成工具的工具)。 :-)
局限: 如果列表不是在编译时创建的,而是在程序运行时计算或接收的,那么这种方法不适合。此外,如果这个列表经常变化,那么生成哈希函数和代码所需的计算时间可能会使这种方法不适合。
【讨论】:
由于不知道给定列表中的最大值,如何有效地确定位向量的大小? @Vikdor:我不知道你为什么说最大值未知,而问题清楚地说“列表是固定的”。 那么,您将遍历列表以找到导致 O(n) 的最大值,这样可以吗?明白了。 @Vikdor:但你只需要这样做一次,而不是每次查找。因此摊销成本变为 O(1)。 因为查找次数将以百万为单位。像排序这样的预处理是可以接受的【参考方案3】: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;
;
【讨论】:
以上是关于从整数范围内搜索的主要内容,如果未能解决你的问题,请参考以下文章
lucene-查询query->RangeQuery在某一范围内搜索