我正在寻找一种在C ++中有效规范化数组的方法,规范化意味着将所有数组值转换为小于或等于n的值。所以这个:
5235 223 1000 40 40
变为:
4 2 3 1 1
或3 1 2 0 0
这是我的代码
vector<int> normalize_array(vector<int> arr){
vector<int> tmp(arr), ret(arr.size());
sort(tmp.begin(), tmp.end());
for (int i = 0; i < arr.size(); ++i){
vector<int>::iterator iter = find(tmp.begin(), tmp.end(), arr[i]);
ret[i] = std::distance(tmp.begin(), iter);
}
return ret;
}
输出为4 2 3 0 0
,上述代码无法很好地处理重复元素。
有没有更好的方法呢?
答案 0 :(得分:3)
应用评论中所述的调整,并使用C ++ lambdas:
vector<int> normalize_array(const vector<int> &arr /* O(1) */) {
vector<int> tmp(arr) /* O(N) */, ret(arr.size()) /* O(1) */;
sort(tmp.begin(), tmp.end()); // O(N lg N)
transform(arr.cbegin(), arr.cend(), ret.begin(), [&tmp](int x) {
return distance(tmp.begin(), lower_bound(tmp.begin(), tmp.end(), x));
}); // O(N lg N)
return ret; // O(1) by move semantics
} // O(1) + O(N) + O(1) + O(N lg N) + O(N lg N) == O(N lg N)
在下面的解决方案中,灵感来自@ sachse的答案,但使用C ++ 11,通过正确的规范化修复问题,生成4 2 3 1 1
,因为我相信是预期的:
vector<int> normalize_array(const vector<int> &arr) {
if (arr.empty())
return {};
vector<int> idx(arr.size()), ret(arr.size());
iota(idx.begin(), idx.end(), 0);
sort(idx.begin(), idx.end(),
[&arr](int i, int j) { return arr[i] < arr[j]; });
ret[idx[0]] = 1;
for (size_t i = 1; i < arr.size(); ++i) {
ret[idx[i]] = ret[idx[i - 1]] + (arr[idx[i]] != arr[idx[i - 1]]);
}
return ret;
}
答案 1 :(得分:1)
如果你用这种方式定义规范化(数学家可能会说规范化是完全不同的),它就成了排序的问题(你正在有效地创建一个升序值的索引数组)。所以我想你应该看看排序算法并将它们用于你的情况。
您只需要考虑具有相同值的元素具有相同的索引 - 通常排序算法不会这样做。
答案 2 :(得分:1)
我建议使用以下具有O(n log n)复杂度的解决方案。唯一剩下的问题是,该算法不处理具有相同标准化值的重复值。
struct IndexComp {
IndexComp(const std::vector<int>& vec) : m_vec(vec) {}
bool operator() (int i,int j) { return (m_vec[i] < m_vec[j]);}
const std::vector<int>& m_vec;
};
std::vector<int> normalize_array(const std::vector<int>& arr){
std::vector<int> tmp, ret(arr.size());
IndexComp indexComp(arr);
for (int i = 0; i < arr.size(); ++i){
tmp.push_back(i);
}
std::sort(tmp.begin(), tmp.end(), indexComp);
for (int i = 0; i < arr.size(); ++i){
ret[tmp[i]] = i;
}
return ret;
}
答案 3 :(得分:1)
这是一个O(n log n)
解决方案,主要是基于标准库的解决方案,它处理OP中示例所暗示的重复值(尽管它来自0
,而不是{{ 1}},因此示例输入产生1
:
3 1 2 0 0
它不是直接排序,而是将所有元素放入临时地图中。对它们进行排序并消除重复(template<typename It, typename OutIt>
void normalize_array(It b, It e, OutIt out) {
using T = typename It::value_type;
std::map<T, int> tmp;
std::transform(b, e, std::inserter(tmp, tmp.begin()),
[](T v){ return std::make_pair(v, 0); });
int i = 0; for (auto& ent : tmp) ent.second = i++;
std::transform(b, e, out,
[&](T v){ return tmp[v]; });
}
);我本可以使用一个集合,但是我想要下一步的映射,即按顺序对值进行编号(O(n log n)
)。然后,映射可用于查找每个值的索引。 (O(n)
)。
虽然解决方案在复杂性方面是最佳的,但可能有办法降低常数。
上查看