如何按特定的预定顺序对字符串向量进行排序?

时间:2018-11-23 10:05:08

标签: c++ string algorithm sorting vector

问题:我需要以确切的特定顺序对字符串向量进行排序。假设我们有一个常数向量或具有精确顺序的数组:

vector<string> correctOrder = {"Item3", "Item1", "Item5", "Item4", "Item2"};

接下来,我们有一个动态的传入向量,该向量将具有相同的Item,但它们可能是混合的并且数量较少。

vector<string> incommingVector = {"Item1", "Item5", "Item3"};

因此,我需要按照第一个向量incomming的顺序对correctOrder向量进行排序,结果必须为:

vector<string> sortedVector = {"Item3", "Item1", "Item5"};

我认为正确的顺序可能以不同的方式表示,但无法弄清楚。 有人可以帮我吗?

5 个答案:

答案 0 :(得分:10)

如果默认比较还不够(字典比较),那么您可以做的最简单的事情就是为sort函数提供一个lambda,告诉它哪个字符串在前。 您可以将correctorder向量中的字符串作为键,并将它们在排序数组中的对应位置作为值的unordered_map<string,int>

cmp函数将只比较您在incommingVector中提供的键的值。

unordered_map<string, int> my_map;
for(int i = 0 ; i < correctorder.size() ; i++)
   my_map[correctorder[i]]=i;

auto cmp =[&my_map](const string& s, const string& s1){
   return my_map[s] < my_map[s1];
}   

sort(incommingVector.begin(), incommingVector.end() , cmp);

答案 1 :(得分:3)

您可以利用std::unordered_map<std::string, int>的优势,即用于在恒定时间内将字符串映射为整数的哈希表。您可以使用它来找出给定的字符串在correctOrder中的向量O(1)中所占的位置,以便可以在恒定时间内比较向量incomming中的两个字符串。

考虑以下功能sort_incomming_vector()

#include <unordered_map>

using Vector = std::vector<std::string>;

void sort_incomming_vector(const Vector& correctOrder /*N*/, Vector& incomming /*M*/)
{
   std::unordered_map<std::string, int> order;

   // populate the order hash table in O(N) time
   for (size_t i = 0; i < correctOrder.size(); ++i)
      order[correctOrder[i]] = i;

   // sort "incomming" in O(M*log M) time
   std::sort(incomming.begin(), incomming.end(),
            [&order](const auto& a, const auto& b) { // sorting criterion
               return order[a] < order[b];
            }
   ); 
}

哈希表order 映射为整数,该结果整数将被传递给排序算法{{3}的lambda(即排序标准)使用},以比较向量incomming中的一对字符串,以便排序算法可以相应地对它们进行置换。

如果correctOder包含N个元素,并且incomming包含M个元素,则可以在O(N)时间和{{1 }}可以在incomming时间内排序。因此,整个算法将在O(M*log M)时间运行。

如果O(N + M*log M)N大得多,则此解决方案是最佳的,因为主导项将是M,即NO(N + M*log M)

答案 2 :(得分:3)

您可以创建自己的函子,以按照以下代码说明的模板矢量顺序对矢量进行排序:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
struct MyComparator
{
    //static const int x = 9;
  const std::vector<std::string> correctOrder{"Item1", "Item2", "Item3", "Item4", "Item5"};
  bool operator() (const std::string& first,const std::string& second )
  {
      auto firstitr = std::find(correctOrder.begin(),correctOrder.end(),first);
      auto seconditr = std::find(correctOrder.begin(),correctOrder.end(),second);
      return firstitr < seconditr;
  }
};
void printVector(const std::vector<std::string>& input)
{
    for(const auto&elem:input)
    {
        std::cout<<elem<<" , ";
    }
    std::cout<<std::endl;
}
int main()
{
  std::vector<string> incomingVector = {"Item3", "Item5", "Item1"};
  std::cout<<"vector before sort... "<<std::endl;
  printVector(incomingVector);
  std::sort(incomingVector.begin(),incomingVector.end(),MyComparator());
  std::cout<<"vector after sort...."<<std::endl;
  printVector(incomingVector);
  return 0;
}

答案 3 :(得分:1)

您需要创建一个比较函数,该函数返回正确的顺序并将其传递给std::sort。为此,您可以编写一个可重用的函数,该函数返回一个lambda,该lambda将对尝试std::find的两个元素进行比较的结果进行比较。 std::find返回迭代器,您可以将其与<运算符进行比较。

#include <algorithm>

std::vector<std::string> correctOrder = {"Item1", "Item2", "Item3", "Item4", "Item5"};
// Could be just std::string correctOrder[], or std::array<...> etc.

// Returns a sorter that orders elements based on the order given by the iterator pair
// (so it supports not just std::vector<string> but other containers too.
template <typename ReferenceIter>
auto ordered_sorter(ReferenceIter ref_begin, ReferenceIter ref_end) {
    // Note: you can build an std::unordered_map<ReferenceIter::value_type, std::size_t> to
    // be more efficient and compare map.find(left)->second with 
    // map.find(right)->second (after you make sure the find does not return a
    // one-past-the-end iterator.
    return [&](const auto& left, const auto& right) {
        return std::find(ref_begin, ref_end, left) < std::find(ref_begin, ref_end, right);
    };
}

int main() {
    using namespace std;
    vector<string> v{"Item3", "Item5", "Item1"};

    // Pass the ordered_sorter to std::sort
    std::sort(v.begin(), v.end(), ordered_sorter(std::begin(correctOrder), std::end(correctOrder)));
    for (const auto& s : v)
        std::cout << s << ", "; // "Item1, Item3, Item5, "
}

请注意,对于大量元素,此答案的效率较低,但比使用std::unordered_map<std::string, int>进行查找的解决方案更简单,但是对于少量元素,线性搜索可能会更快。如果性能很重要,请进行基准测试。

答案 4 :(得分:-3)

编辑:如果您不希望使用默认比较,则需要将自定义比较方法作为第三个参数传递,如链接参考中存在的示例所示。

使用std::sort,您已完成:

#include <iostream>     // std::cout
#include <algorithm>    // std::sort
#include <vector>       // std::vector
#include <string>       // std::string
using namespace std;

int main () {
  vector<string> incommingVector = {"Item3", "Item5", "Item1"};

  // using default comparison (operator <):
  std::sort (incommingVector.begin(), incommingVector.end());

  // print out content:
  std::cout << "incommingVector contains:";
  for (std::vector<string>::iterator it=incommingVector.begin(); it!=incommingVector.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}

输出:

  

incommingVector包含:Item1 Item3 Item5