【问题标题】:How to sort a vector of strings in a specific predetermined order?如何按特定的预定顺序对字符串向量进行排序?
【发布时间】:2019-04-25 22:32:01
【问题描述】:

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

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

接下来,我们有一个动态的传入向量,它将具有相同的项目,但它们可能混合在一起并且数量更少。

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

所以我需要像第一个向量correctOrder一样的顺序对incomming向量进行排序,结果一定是:

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

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

【问题讨论】:

  • 使用std::sort 进行实际排序是一个好的开始。我还建议阅读 lambda expressions 以提供自定义比较函数,您可以将其传递给 std::sort 使用。
  • @gsamaras 也许吧。或者,也许 OP 想要使用向量 correctOrder 中的元素来获取元素的相对位置?我不知道,从这个问题上看不太清楚。
  • 这是正确的 gsamaras。我需要相对于第一个向量进行排序。
  • 我看到所有的答案都是离线的。有没有更高效的在线算法?
  • 虽然所有答案都告诉你如何在 O(N * M log M) 中解决,但你可以通过使用堆在 O(log N * M log M) 中实现这一点,因为它们只需要记录 N 次插入。请注意,无法查找中间的值,因为那样您将不得不花费 M 时间。

标签: c++ string algorithm sorting vector


【解决方案1】:

如果默认比较不够(字典比较),那么您可以做的最简单的事情是为排序函数提供lambda,告诉它哪个字符串先出现。 你可以有一个unordered_map&lt;string,int&gt;,其中correctorder向量中的字符串作为键,它们在排序数组中的对应位置作为值。

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);

【讨论】:

    【解决方案2】:

    您可以创建自己的仿函数来按照模板向量顺序对向量进行排序,如下代码所述:

    #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;
    }
    

    【讨论】:

    • 在我的例子中,正好有 8 个元素,这个解决方案非常有效。非常感谢!!
    【解决方案3】:

    您可以利用std::unordered_map&lt;std::string, int&gt;,即在恒定时间内将字符串映射为整数的哈希表。您可以使用它来找出给定字符串在O(1) 中的向量correctOrder 中所占的位置,以便您可以在恒定时间内比较向量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将字符串映射成整数,这个结果整数被传递给排序算法std::sort的lambda(即排序标准)用于比较向量incomming中的一对字符串,以便排序算法可以相应地排列它们。

    如果correctOder包含N元素,incomming包含M元素,则哈希表可以在O(N)时间初始化,incomming可以在O(M*log M)时间排序。因此,整个算法将在O(N + M*log M)时间运行。

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

    【讨论】:

      【解决方案4】:

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

      #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&lt;std::string, int&gt; 进行查找的解决方案更简单,但对于少量元素,线性搜索可能更快。如果性能很重要,请进行基准测试。

      【讨论】:

        【解决方案5】:

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

        使用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

        【讨论】:

        • Okey 但是可以说正确的顺序是 {"Item4", "Item1", "Item3", "Item2", "Item5"};会这样工作吗?
        • @RosenKaradinev 因为您需要使用自定义比较函数,您将作为std::sort() 的第三个参数传递。有关更多信息,请参阅我链接到我的答案的参考。
        • 我认为你误读了这个问题,并且 OP 没有使用最好的例子(对于问题中的例子,一个简单的排序就可以了,但不是在一般情况下)
        • 不是问题的答案
        • 感谢@RosenKaradinev 我现在明白了这个问题!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-01-21
        • 1970-01-01
        • 2011-10-03
        • 1970-01-01
        • 1970-01-01
        • 2013-07-19
        • 1970-01-01
        相关资源
        最近更新 更多