对象向量的最小划分(C++)
Minimally partitioning a vector of objects (C++)
我有一个对象的std::vector
,其中向量中的每个元素或多或少看起来像:
struct Obj {
int group;
};
矢量中的条目没有特定的顺序。通常,在分区时,人们可能希望通常将具有共同点的同一分区中的元素分组,然而,在我的情况下,我想要的是重新排列这个向量中的条目,并以使用绝对最小分区数的方式对其进行分区,其中单个分区中的每个元素与同一分区中的其他元素属于不同的组。
这是否可以在不迭代向量的每一个排列并查看每个排列有多少分区的情况下实现?
编辑:
有人问我一个例子,所以我将尝试提供一个。
如果对象的初始矢量是
[ {1}, {2}, {3}, {2}, {3}, {1}, {4}, {5}, {3}, {2} ]
最佳分区是将其分为三个分区,如下所示:
[ {1}, {2}, {3}, {4}, {5} ] [ {1}, {2}, {3} ] [{2}, {3} ]
因此,在每个分区中,所有条目都属于不同的组。
如果我正确理解您的需求,那么"最小分区数"由原始向量中单个值的最大频率给出。因此,您可以创建一个直方图,然后在其中找到最大条目。(这是向量大小的线性。)现在创建m向量(其中m是刚刚确定的最大频率),并将每个m-相同的值分配给其中一个。可以保证您可以以一种不会在分区中出现重复的方式来分配剩余的元素。
在输入向量v的伪码中,大小n:
- 初始化一个空直方图H
- 对于v中的每个x项:
- 增量Hx202F;[x202F;xx202F;乘以1,如果没有这样的bin,则零之前初始化它
- m&lar;H中的最大频率
- 初始化空向量v1,…,vm
- 对于每个值x;[x202F;xx202F≥0:
- 对于i&lar;1到Hx202F;[x202F;xx202F:
- 将x附加到vi
- 对于i&lar;1到Hx202F;[x202F;xx202F:
请注意,如果向量中的对象具有确定它们是否等于其仅的数据成员的键,则此操作效果良好。然而,如果他们有更多的状态需要保留,但没有参与确定平等,那么程序很容易调整以考虑到这一点。
- 初始化一个空直方图H
- 对于v中的每个x项:
- 增量Hx202F;[键(x);乘以1,如果没有这样的bin,则零之前初始化它
- m&lar;H中的最大频率
- 初始化空向量v1,…,vm
- 对于v中的每个值x:
- i&lar Hx202F;[键(x)
- 将x附加到vi
- 递减Hx202F;[键(x);一个
如果您想要快速解决方案,可以使用std::unordered_map<int, int>
作为直方图。
以下是一个(最终有点过于泛化)实现在C++14中的样子。
#include <algorithm> // std::max_element
#include <functional> // std::hash, std::equal_to
#include <iterator> // std::iterator_traits
#include <unordered_map> // std::unordered_map
#include <vector> // std::vector
template<typename FwdIterT,
typename ValueT = typename std::iterator_traits<FwdIterT>::value_type,
typename ValueHashT = std::hash<ValueT>,
typename ValueEqCmpT = std::equal_to<ValueT>>
decltype(auto)
min_partition(const FwdIterT begin, const FwdIterT end)
{
std::vector<std::vector<ValueT>> partitions {};
std::unordered_map<ValueT, int, ValueHashT, ValueEqCmpT> histo {};
for (auto iter = begin; iter != end; ++iter)
histo[*iter]++;
const auto cmpfreq = [](const auto& lhs, const auto& rhs){
return lhs.second < rhs.second;
};
const auto maxbin = std::max_element(histo.cbegin(), histo.cend(), cmpfreq);
partitions.resize(maxbin->second);
for (auto iter = begin; iter != end; ++iter)
partitions.at(histo.at(*iter)-- - 1).push_back(*iter);
return partitions;
}
它可以这样使用。
#include <iostream> // std::cout
#include <string> // std::string
#include <utility> // std::begin, std::end
int
main(int argc, char * * argv)
{
using std::begin;
using std::end;
for (int i = 1; i < argc; ++i)
{
const std::string text {argv[i]};
const auto partitions = min_partition(begin(text), end(text));
std::cout << "input: " << text << "n";
std::cout << "output: " << partitions.size() << " partitionsnn";
for (auto it1 = begin(partitions); it1 != end(partitions); ++it1)
{
std::cout << "[";
for (auto it2 = begin(*it1); it2 != end(*it1); ++it2)
std::cout << (it2 != begin(*it1) ? ", " : "") << *it2;
std::cout << "]n";
}
if (i != argc - 1)
std::cout << "nn";
}
}
如果给定一些众所周知的字符串作为输入,它将产生以下输出。
input: WEWEREARRESTEDAFTERDADATEDEEREGGS
output: 10 partitions
[W, F, A, T, D, R, E, G, S]
[W, S, T, R, A, D, E, G]
[R, T, A, D, E]
[A, R, D, E]
[R, E]
[E]
[E]
[E]
[E]
[E]
input: ALASDADHADAGLASSSALAD
output: 8 partitions
[H, G, S, L, A, D]
[D, L, S, A]
[L, D, A, S]
[S, D, A]
[A]
[A]
[A]
[A]
input: THEQUICKBROWNFOXJUMPSOVERTHESLEAZYDOG
output: 4 partitions
[Q, I, C, K, B, W, N, F, X, J, U, M, P, V, R, T, H, S, L, E, A, Z, Y, D, O, G]
[T, H, U, R, S, O, E]
[O, E]
[E, O]
最简单的方法可能是以下算法(伪代码):
std::vector<std::vector<Obj>> partitions;
sort(yourVector);
for (each group of equal Obj) {
if(sizeOfThisGroup > partitions.size())
add enough partitions
split the group into the partitions
}
这在O(nlog(n))
中运行。如果最多m Obj
相等,那么最终将得到完全相同的m
分区。这显然是最低限度的。
- 如何将输出数字划分为奇数和偶数
- C++无符号短裤的划分导致 int
- 如何在 C++ 中将数组划分为不同的数组
- 分类还是划分枚举?
- 如何将代码划分为多个.cpp文件 C++.
- 将一系列已排序的元素划分为相邻组
- 在某个位置划分链表
- c++中向量中元素的划分
- 变量和常数的C++划分不起作用
- 如何在C++中划分元组
- 定义整数之间的特殊划分
- 为什么不按默认构造路径划分路径只是在 Visual Studio 中添加尾随分隔符?
- C++ 按非类型参数类型划分的模板专用化
- 划分和乘以STD :: Chrono ::持续时间
- 将数字划分为直到满足条件= 0
- 如果类划分为单独的文件,则不调用默认构造函数
- 疯狂(乘,添加,划分)哈希功能如何起作用
- 为什么 omp 并行部分中的线程不会在其部分上划分?
- 我如何使我的循环转到char*阵列的尽头,并将每个城市都放在链接的列表中(城市被划分为白色的空间)
- 如何将一组二进制树的迭代器划分