Answers:
使用C++
11个lambda:
#include <iostream>
#include <vector>
#include <numeric> // std::iota
#include <algorithm> // std::sort, std::stable_sort
using namespace std;
template <typename T>
vector<size_t> sort_indexes(const vector<T> &v) {
// initialize original index locations
vector<size_t> idx(v.size());
iota(idx.begin(), idx.end(), 0);
// sort indexes based on comparing values in v
// using std::stable_sort instead of std::sort
// to avoid unnecessary index re-orderings
// when v contains elements of equal values
stable_sort(idx.begin(), idx.end(),
[&v](size_t i1, size_t i2) {return v[i1] < v[i2];});
return idx;
}
现在,您可以在迭代中使用返回的索引向量,例如
for (auto i: sort_indexes(v)) {
cout << v[i] << endl;
}
您还可以选择提供原始索引向量,排序函数,比较器,或使用额外的向量在sort_indexes函数中自动重新排序v。
for (size_t i = 0; i != idx.size(); ++i) idx[i] = i;
我更喜欢手工,而不是手工制作std::iota( idx.begin(), idx.end(), 0 );
#include <numeric>
了丝毫()
iota
是整个C ++标准库中命名最少的算法。
您可以对std :: pair而不是int进行排序-第一个int是原始数据,第二个int是原始索引。然后提供一个仅在第一个int上排序的比较器。例:
Your problem instance: v = [5 7 8]
New problem instance: v_prime = [<5,0>, <8,1>, <7,2>]
使用比较器对新问题实例进行排序,例如:
typedef std::pair<int,int> mypair;
bool comparator ( const mypair& l, const mypair& r)
{ return l.first < r.first; }
// forgetting the syntax here but intent is clear enough
使用该比较器的v_prime上std :: sort的结果应为:
v_prime = [<5,0>, <7,2>, <8,1>]
您可以通过遍历向量,从每个std :: pair抓住.second来剥离索引。
假设给定向量为
A=[2,4,3]
创建一个新载体
V=[0,1,2] // indicating positions
对V进行排序,并在排序时代替比较V的元素,比较A的对应元素
//Assume A is a given vector with N elements
vector<int> V(N);
int x=0;
std::iota(V.begin(),V.end(),x++); //Initializing
sort( V.begin(),V.end(), [&](int i,int j){return A[i]<A[j];} );
std::iota()
更优雅的初始化方式map
我写了索引排序的通用版本。
template <class RAIter, class Compare>
void argsort(RAIter iterBegin, RAIter iterEnd, Compare comp,
std::vector<size_t>& indexes) {
std::vector< std::pair<size_t,RAIter> > pv ;
pv.reserve(iterEnd - iterBegin) ;
RAIter iter ;
size_t k ;
for (iter = iterBegin, k = 0 ; iter != iterEnd ; iter++, k++) {
pv.push_back( std::pair<int,RAIter>(k,iter) ) ;
}
std::sort(pv.begin(), pv.end(),
[&comp](const std::pair<size_t,RAIter>& a, const std::pair<size_t,RAIter>& b) -> bool
{ return comp(*a.second, *b.second) ; }) ;
indexes.resize(pv.size()) ;
std::transform(pv.begin(), pv.end(), indexes.begin(),
[](const std::pair<size_t,RAIter>& a) -> size_t { return a.first ; }) ;
}
用法与std :: sort相同,不同之处在于索引容器接收排序的索引。测试:
int a[] = { 3, 1, 0, 4 } ;
std::vector<size_t> indexes ;
argsort(a, a + sizeof(a) / sizeof(a[0]), std::less<int>(), indexes) ;
for (size_t i : indexes) printf("%d\n", int(i)) ;
您应该得到2 1 0 3.对于不支持c ++ 0x的编译器,将lamba表达式替换为类模板:
template <class RAIter, class Compare>
class PairComp {
public:
Compare comp ;
PairComp(Compare comp_) : comp(comp_) {}
bool operator() (const std::pair<size_t,RAIter>& a,
const std::pair<size_t,RAIter>& b) const { return comp(*a.second, *b.second) ; }
} ;
并重写std :: sort为
std::sort(pv.begin(), pv.end(), PairComp(comp)()) ;
vector<pair<int,int> >a;
for (i = 0 ;i < n ; i++) {
// filling the original array
cin >> k;
a.push_back (make_pair (k,i)); // k = value, i = original index
}
sort (a.begin(),a.end());
for (i = 0 ; i < n ; i++){
cout << a[i].first << " " << a[i].second << "\n";
}
现在a
在排序中同时包含我们的值和它们各自的索引。
a[i].first = value
在i
。
a[i].second = idx
在初始数组中。
我遇到了这个问题,并发现直接对迭代器进行排序将是对值进行排序并跟踪索引的一种方式。无需定义pair
s的(value,index)的额外容器,这在值是大对象时很有用;迭代器提供对值和索引的访问:
/*
* a function object that allows to compare
* the iterators by the value they point to
*/
template < class RAIter, class Compare >
class IterSortComp
{
public:
IterSortComp ( Compare comp ): m_comp ( comp ) { }
inline bool operator( ) ( const RAIter & i, const RAIter & j ) const
{
return m_comp ( * i, * j );
}
private:
const Compare m_comp;
};
template <class INIter, class RAIter, class Compare>
void itersort ( INIter first, INIter last, std::vector < RAIter > & idx, Compare comp )
{
idx.resize ( std::distance ( first, last ) );
for ( typename std::vector < RAIter >::iterator j = idx.begin( ); first != last; ++ j, ++ first )
* j = first;
std::sort ( idx.begin( ), idx.end( ), IterSortComp< RAIter, Compare > ( comp ) );
}
至于用法示例:
std::vector < int > A ( n );
// populate A with some random values
std::generate ( A.begin( ), A.end( ), rand );
std::vector < std::vector < int >::const_iterator > idx;
itersort ( A.begin( ), A.end( ), idx, std::less < int > ( ) );
现在,例如,排序向量中的第5个最小元素将具有值,**idx[ 5 ]
并且其在原始向量中的索引将为distance( A.begin( ), *idx[ 5 ] )
或简单*idx[ 5 ] - A.begin( )
。
还有一种解决方法,使用地图:
vector<double> v = {...}; // input data
map<double, unsigned> m; // mapping from value to its index
for (auto it = v.begin(); it != v.end(); ++it)
m[*it] = it - v.begin();
但是,这将消除非唯一元素。如果不可接受,请使用多图:
vector<double> v = {...}; // input data
multimap<double, unsigned> m; // mapping from value to its index
for (auto it = v.begin(); it != v.end(); ++it)
m.insert(make_pair(*it, it - v.begin()));
为了输出索引,请遍历地图或多地图:
for (auto it = m.begin(); it != m.end(); ++it)
cout << it->second << endl;
@Lukasz Wiklendt的精美解决方案!尽管就我而言,我需要一些更通用的东西,所以我对其进行了一些修改:
template <class RAIter, class Compare>
vector<size_t> argSort(RAIter first, RAIter last, Compare comp) {
vector<size_t> idx(last-first);
iota(idx.begin(), idx.end(), 0);
auto idxComp = [&first,comp](size_t i1, size_t i2) {
return comp(first[i1], first[i2]);
};
sort(idx.begin(), idx.end(), idxComp);
return idx;
}
示例:查找索引,该索引按长度对字符串向量进行排序,但第一个元素是虚拟对象除外。
vector<string> test = {"dummy", "a", "abc", "ab"};
auto comp = [](const string &a, const string& b) {
return a.length() > b.length();
};
const auto& beginIt = test.begin() + 1;
vector<size_t> ind = argSort(beginIt, test.end(), comp);
for(auto i : ind)
cout << beginIt[i] << endl;
印刷品:
abc
ab
a
考虑使用std::multimap
@Ulrich Eckhardt建议的方法。只是代码可以变得更简单。
给定
std::vector<int> a = {5, 2, 1, 4, 3}; // a: 5 2 1 4 3
按插入的平均时间排序
std::multimap<int, std::size_t> mm;
for (std::size_t i = 0; i != a.size(); ++i)
mm.insert({a[i], i});
检索值和原始索引
std::vector<int> b;
std::vector<std::size_t> c;
for (const auto & kv : mm) {
b.push_back(kv.first); // b: 1 2 3 4 5
c.push_back(kv.second); // c: 2 1 4 3 0
}
选择a std::multimap
而不是a 的原因std::map
是允许原始向量中的值相等。另请注意,与for不同std::map
,operator[]
它没有为定义std::multimap
。
创建一个std::pair
in函数,然后排序对:
通用版本:
template< class RandomAccessIterator,class Compare >
auto sort2(RandomAccessIterator begin,RandomAccessIterator end,Compare cmp) ->
std::vector<std::pair<std::uint32_t,RandomAccessIterator>>
{
using valueType=typename std::iterator_traits<RandomAccessIterator>::value_type;
using Pair=std::pair<std::uint32_t,RandomAccessIterator>;
std::vector<Pair> index_pair;
index_pair.reserve(std::distance(begin,end));
for(uint32_t idx=0;begin!=end;++begin,++idx){
index_pair.push_back(Pair(idx,begin));
}
std::sort( index_pair.begin(),index_pair.end(),[&](const Pair& lhs,const Pair& rhs){
return cmp(*lhs.second,*rhs.second);
});
return index_pair;
}
好吧,我的解决方案使用残留技术。我们可以将排序后的值放在高2个字节中,将元素的索引放在低2个字节中:
int myints[] = {32,71,12,45,26,80,53,33};
for (int i = 0; i < 8; i++)
myints[i] = myints[i]*(1 << 16) + i;
然后myints
照常对数组进行排序:
std::vector<int> myvector(myints, myints+8);
sort(myvector.begin(), myvector.begin()+8, std::less<int>());
之后,您可以通过残差访问元素的索引。以下代码显示按升序排序的值的索引:
for (std::vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it)
std::cout << ' ' << (*it)%(1 << 16);
当然,此技术仅适用于原始数组中相对较小的值myints
(即那些可以放入的高2个字节的值int
)。但是,这样做还有另一个好处:区分的相同值myints
:它们的索引将以正确的顺序打印。
对于此类问题,将原始数组数据存储到新数据中,然后对排序后的数组的第一个元素进行二进制搜索到重复的数组中,然后将索引存储到向量或数组中。
input array=>a
duplicate array=>b
vector=>c(Stores the indices(position) of the orignal array
Syntax:
for(i=0;i<n;i++)
c.push_back(binarysearch(b,n,a[i]));`
这里的binarysearch是一个函数,它接受数组,数组的大小,搜索项并返回搜索项的位置
有很多方法。一个相当简单的解决方案是使用2D向量。
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<vector<double>> val_and_id;
val_and_id.resize(5);
for (int i = 0; i < 5; i++) {
val_and_id[i].resize(2); // one to store value, the other for index.
}
// Store value in dimension 1, and index in the other:
// say values are 5,4,7,1,3.
val_and_id[0][0] = 5.0;
val_and_id[1][0] = 4.0;
val_and_id[2][0] = 7.0;
val_and_id[3][0] = 1.0;
val_and_id[4][0] = 3.0;
val_and_id[0][1] = 0.0;
val_and_id[1][1] = 1.0;
val_and_id[2][1] = 2.0;
val_and_id[3][1] = 3.0;
val_and_id[4][1] = 4.0;
sort(val_and_id.begin(), val_and_id.end());
// display them:
cout << "Index \t" << "Value \n";
for (int i = 0; i < 5; i++) {
cout << val_and_id[i][1] << "\t" << val_and_id[i][0] << "\n";
}
return 0;
}
这是输出:
Index Value
3 1
4 3
1 4
0 5
2 7