假设:
struct Object {
int id;
...
};
list<Object> objectList;
list<int> idList;
根据objectList
的顺序订购idList
的最佳方法是什么?
示例(伪代码):
INPUT
objectList = {o1, o2, o3};
idList = {2, 3, 1};
ACTION
sort(objectList, idList);
OUTPUT
objectList = {o2, o3, o1};
我在文档中进行了搜索,但我只找到了对元素进行比较的方法。
答案 0 :(得分:2)
您可以将对象存储在std::map
中,并以id
为键。然后遍历idList
,使用map
从id
中获取对象。
std::map<int, Object> objectMap;
for (auto itr = objectList.begin(); itr != objectList.end(); itr++)
{
objectMap.insert(std::make_pair(itr->id, *itr));
}
std::list<Object> newObjectList;
for (auto itr = idList.begin(); itr != idList.end(); itr++)
{
// here may fail if your idList contains ids which does not appear in objectList
newObjectList.push_back(objectMap[*itr]);
}
// now newObjectList is sorted as order in idList
答案 1 :(得分:2)
这是另一种变体,适用于O(n log n)
。这是渐近最优的。
#include <list>
#include <vector>
#include <algorithm>
#include <iostream>
#include <cassert>
int main() {
struct O {
int id;
};
std::list<O> object_list{{1}, {2}, {3}, {4}};
std::list<int> index_list{4, 2, 3, 1};
assert(object_list.size() == index_list.size());
// this vector is optional. It is needed if sizeof(O) is quite large.
std::vector<std::pair<int, O*>> tmp_vector(object_list.size());
// this is O(n)
std::transform(begin(object_list), end(object_list), begin(tmp_vector),
[](auto& o) { return std::make_pair(o.id, &o); });
// this is O(n log n)
std::sort(begin(tmp_vector), end(tmp_vector),
[](const auto& o1, const auto& o2) {
return o1.first < o2.first;
});
// at this point, tmp_vector holds pairs in increasing index order.
// Note that this may not be a contiguous list.
std::list<O> tmp_list(object_list.size());
// this is again O (n log n), because lower_bound is O (n)
// we then insert the objects into a new list (you may also use some
// move semantics here).
std::transform(begin(index_list), end(index_list), begin(tmp_list),
[&tmp_vector](const auto& i) {
return *std::lower_bound(begin(tmp_vector), end(tmp_vector),
std::make_pair(i, nullptr),
[](const auto& o1, const auto& o2) {
return o1.first < o2.first;
})->second;
});
// As we just created a new list, we swap the new list with the old one.
std::swap(object_list, tmp_list);
for (const auto& o : object_list)
std::cout << o.id << std::endl;
}
我认为O非常大而且不易移动。因此,我首先创建仅包含对的tmp_vector
。然后我对这个向量进行排序。
之后我可以简单地浏览index_list
并使用二分查找找到匹配的索引。
让我详细说明为什么地图不是最好的解决方案,尽管你得到的是一小段代码。如果使用地图,则需要在每次插入后重新平衡树。这不会花费太多的代价(因为n次重新平衡会使您与排序一次相同),但常数会更大。 A&#34;常量地图&#34;没有多大意义(除了访问它可能更容易)。
然后,我将&#34;简单&#34;地图方法反对我的&#34;不那么简单&#34;向量的方法。我创建了一个随机排序的index_list
条目N
。这就是我得到的(在我们身上):
N map vector
1000 90 75
10000 1400 940
100000 24500 15000
1000000 660000 250000
注意:此测试显示最坏的情况,因为在我的情况下,只有index_list被随机排序,而object_list(按顺序插入到地图中)被排序。所以重新平衡显示了它的所有效果。如果object_list是随机的,性能将表现得更相似,尽管性能总是更差。当对象列表完全随机时,向量列表甚至会表现得更好。
因此已经有1000个条目,差异已经非常大了。因此,我强烈投票支持基于矢量的方法。
答案 2 :(得分:1)
假设从外部处理数据并且您无法选择容器:
assert( objectList.size() == idList.size() );
std::vector<std::pair<int,Object>> wrapper( idList.size() );
auto idList_it = std::begin( idList );
auto objectList_it = std::begin( objectList );
for( auto& e: wrapper )
e = std::make_pair( *idList_it++, *objectList_it++ );
std::sort(
std::begin(wrapper),
std::end(wrapper),
[]
(const std::pair<int,Object>& a, const std::pair<int,Object>& b) -> bool
{ return a.first<b.first; }
);
然后,复制回原始容器。
{
auto objectList_it = std::begin( objectList );
for( const auto& e: wrapper )
*objectList_it++ = e;
}
但是这个解决方案并不是最优的,我确信有人会提供更好的解决方案。
编辑:对的默认比较运算符要求为第一个和第二个成员定义它。因此,最简单的方法是提供一个lambda。
Edit2 :出于某种原因,如果使用std::list
作为包装器,则不会构建。但是,如果您使用std::vector
(see here)。
答案 3 :(得分:0)
std::list
有一个sort
成员函数,您可以将其与自定义比较函数一起使用。
该自定义仿函数必须在id
中查找对象的idList
,然后可以使用std::distance
来计算idList
中元素的位置。它对两个要比较的对象都这样做,如果第一个位置小于第二个位置,则返回true。
以下是一个例子:
#include <iostream>
#include <list>
#include <algorithm>
#include <stdexcept>
struct Object
{
int id;
};
int main()
{
Object o1 = { 1 };
Object o2 = { 2 };
Object o3 = { 3 };
std::list<Object> objectList = { o1, o2, o3 };
std::list<int> const idList = { 2, 3, 1 };
objectList.sort([&](Object const& first, Object const& second)
{
auto const id_find_iter1 = std::find(begin(idList), end(idList), first.id);
auto const id_find_iter2 = std::find(begin(idList), end(idList), second.id);
if (id_find_iter1 == end(idList) || id_find_iter2 == end(idList))
{
throw std::runtime_error("ID not found");
}
auto const pos1 = std::distance(begin(idList), id_find_iter1);
auto const pos2 = std::distance(begin(idList), id_find_iter2);
return pos1 < pos2;
});
for (auto const& object : objectList)
{
std::cout << object.id << '\n';
}
}
它可能效率不高,但你可能永远不会注意到它。如果它仍然困扰您,您可能希望寻找具有std::vector
的解决方案,这与std::list
不同,提供随机访问迭代器。这将std::distance
从O(n)变为O(1)。
答案 4 :(得分:0)
我觉得最终会遇到这种情况很奇怪,因为我会使用指针代替ID。虽然;可能会有用例。
请注意,在下面的所有示例中,我假设ids-list包含所有id。
您要解决的问题是根据另一个列表中ID的顺序创建/排序对象列表。
这种天真的做法,就是自己写一下:
<div>
<ul data-bind="foreach: shops">
<li>
<span data-bind="text: $data.title, click: $root.shopMarker"></span>
</li>
</ul>
</div>
如果使用排序向量作为输入,则可以优化此代码以获得最佳性能。我把它留给你去做。
对于这个实现,我假设这是std :: vector而不是std :: list,因为这是请求元素索引的更好容器。 (您可以使用更多代码对列表执行相同操作)
void sortByIdVector(std::list<Object> &list, const std::list<int> &ids)
{
auto oldList = std::move(list);
list = std::list<Object>{};
for (auto id : ids)
{
auto itElement = std::find_if(oldList.begin(), oldList.end(), [id](const Object &obj) { return id == obj.id; });
list.emplace_back(std::move(*itElement));
oldList.erase(itElement);
}
}
另一种方法,也更适合std :: vector,只需在正确的位置插入元素,并且比std :: sort更高效。
size_t getIntendedIndex(const std::vector<int> &ids, const Object &obj)
{
auto itElement = std::find_if(ids.begin(), ids.end(), [obj](int id) { return id == obj.id; });
return itElement - ids.begin();
}
void sortByIdVector(std::list<Object> &list, const std::vector<int> &ids)
{
list.sort([&ids](const Object &lhs, const Object &rhs){ return getIntendedIndex(ids, lhs) < getIntendedIndex(ids, rhs); });
}
答案 5 :(得分:0)
<input id="idname" data-bind="value: name, css: name.isDirty() == true ? 'changed' : 'notchanged'" />
我们的想法是检查objectList.sort([&idList] (const Object& o1, const Object& o2) -> bool
{ return std::find(++std::find(idList.begin(), idList.end(), o1.id),
idList.end(), o2.id)
!= idList.end();
});
中o1.id
之前是否找到o2.id
。
我们搜索idList
,增加找到的位置,然后搜索o1.id
:如果找到,则表示o1&lt; O 2。
<强>测试强>
o2.id