[英]Converting sets of integers into ranges
將一組整數轉換為一組范圍的最慣用的方法是什么?
例如,給定{0,1,2,3,4,7,8,9,11}我想得到{{0,4},{7,9},{11,11}}。
假設我們正在從std::set<int>
轉換為std::vector<std::pair<int, int>>
。 我認為Ranges在雙方都是包容性的,因為在我的情況下它更方便,但如果有必要我也可以使用開放式范圍。
我寫了以下功能,但我覺得要重新發明輪子。 請告訴我們STL中有什么東西或者提升它。
typedef std::pair<int, int> Range;
void setToRanges(const std::set<int>& indices, std::vector<Range>& ranges)
{
Range r = std::make_pair(-INT_MAX, -INT_MAX);
BOOST_FOREACH(int i, indices)
{
if (i != r.second + 1)
{
if (r.second >= 0) ranges.push_back(r);
r.first = i;
}
r.second = i;
}
ranges.push_back(r);
}
現在可以使用Boost.ICL中的interval_set(Boost> 1.46)
#include <set>
#include <iostream>
#include <algorithm>
#include <boost/icl/discrete_interval.hpp>
#include <boost/icl/closed_interval.hpp>
#include <boost/icl/interval_set.hpp>
typedef std::set<int> Set;
typedef boost::icl::interval_set<int> IntervalSet;
void setToInterval(const Set& indices, IntervalSet& intervals)
{
Set::const_iterator pos;
for(pos = indices.begin(); pos != indices.end(); ++pos)
{
intervals.insert(boost::icl::construct<boost::icl::discrete_interval<int> >(*pos, *pos, boost::icl::interval_bounds::closed()));
}
}
int main()
{
std::cout << ">>Interval Container Library Rocks! <<\n";
std::cout << "----------------------------------------------------\n";
Set indices = {0, 1, 2, 3, 4, 7, 8, 9, 11};
IntervalSet intervals;
setToInterval(indices, intervals);
std::cout << " intervals joined: " << intervals << "\n";
return 0;
}
輸出:
intervals joined: {[0,4][7,9][11,11]}
我不認為STL或Boost中有任何東西可以做到這一點。
你可以做的一件事是讓你的算法更通用:
template<class InputIterator, class OutputIterator>
void setToRanges(InputIterator first, InputIterator last, OutputIterator dest)
{
typedef std::iterator_traits<InputIterator>::value_type item_type;
typedef typename std::pair<item_type, item_type> pair_type;
pair_type r(-std::numeric_limits<item_type>::max(),
-std::numeric_limits<item_type>::max());
for(; first != last; ++first)
{
item_type i = *first;
if (i != r.second + 1)
{
if (r.second >= 0)
*dest = r;
r.first = i;
}
r.second = i;
}
*dest = r;
}
用法:
std::set<int> set;
// insert items
typedef std::pair<int, int> Range;
std::vector<Range> ranges;
setToRanges(set.begin(), set.end(), std::back_inserter(ranges));
您還應該考慮使用術語interval
而不是range
,因為STL用語中的后者意味着“可以通過迭代器或指針訪問的任何對象序列”( 源 )。
最后,你應該看一下Boost Interval Arithmetic Library ,目前正在審查Boost包含。
我擔心沒有收縮包裹的解決方案,而是另一種算法。
將項目存儲在位向量中 - 如果您知道開始時的最大項目並預先分配向量,則為O(n)。
將該向量轉換為轉換點標志的向量 - 異或 - 具有自身的位移版本的位向量。 稍微偏僻於字邊界,但仍然是O(n)。 從邏輯上講,你在舊的max + 1處獲得一個新密鑰(在所有密鑰耗盡后轉換回零),因此在向量的預分配中允許這樣做是個好主意。
然后,迭代通過位向量查找設置位。 第一個設置位表示范圍的開始,第二個設置位表示下一個范圍的開始,依此類推。 以下bit-fiddling函數(假設32位int)可能有用......
int Low_Bit_No (unsigned int p)
{
if (p == 0) return -1; // No bits set
int l_Result = 31;
unsigned int l_Range = 0xffffffff;
unsigned int l_Mask = 0x0000ffff;
if (p & l_Mask) { l_Result -= 16; } else { l_Mask ^= l_Range; }
l_Range &= l_Mask;
l_Mask &= 0x00ff00ff;
if (p & l_Mask) { l_Result -= 8; } else { l_Mask ^= l_Range; }
l_Range &= l_Mask;
l_Mask &= 0x0f0f0f0f;
if (p & l_Mask) { l_Result -= 4; } else { l_Mask ^= l_Range; }
l_Range &= l_Mask;
l_Mask &= 0x33333333;
if (p & l_Mask) { l_Result -= 2; } else { l_Mask ^= l_Range; }
l_Mask &= 0x55555555;
if (p & l_Mask) { l_Result -= 1; }
return l_Result;
}
我將adjacent_find
與謂詞一起使用,謂詞將“鄰接”定義為兩個不連續的元素。 此解決方案不依賴於INT_MAX。 還是覺得有點笨重。
bool notSequential(int a, int b) { return (a + 1) != b; }
void setToRanges(const std::set<int>& indices, std::vector<Range>& ranges)
{
std::set<int>::iterator iter = indices.begin();
std::set<int>::iterator end = indices.end();
int first;
while (iter != end)
{
first = *iter;
iter = std::adjacent_find(iter, end, notSequential);
if (iter != end)
{
ranges.push_back(std::make_pair(first, *iter));
++iter;
}
}
ranges.push_back(std::make_pair(first, *--iter));
}
這對測試end
超過必要的。 adjacent_find
永遠不能返回列表的最后一個元素,因此遞增的迭代器永遠不會end
,因此仍然可以取消引用。 它可以改寫為:
void setToRanges(const std::set<int>& indices, std::vector<Range>& ranges)
{
std::set<int>::iterator iter = indices.begin();
std::set<int>::iterator end = indices.end();
if (iter == end) return; // empty set has no ranges
int first;
while (true)
{
first = *iter;
iter = std::adjacent_find(iter, end, notSequential);
if (iter == end) break;
ranges.push_back(std::make_pair(first, *iter++));
}
ranges.push_back(std::make_pair(first, *--iter));
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.