【问题标题】:Algorithm to merge multiple sorted sequences into one sorted sequence in C++ [closed]在 C++ 中将多个排序序列合并为一个排序序列的算法[关闭]
【发布时间】:2014-03-30 02:43:52
【问题描述】:

我正在寻找一种算法来合并多个排序序列,假设 X 排序序列与 n 个元素,成一个排序序列在 c++ 中,你能提供一些例子吗?

注意:我不想使用任何库

【问题讨论】:

  • 或许std::merge?
  • @juanchopanza 可能不使用外部库?
  • 我不会认为 C++ 标准库是“外部的”。
  • 如果您有愚蠢的要求,您应该在问题中明确说明。
  • @user2970210 还有,你为什么要问你是否不想使用任何库。否则这一切如何运作?

标签: c++ algorithm merge mergesort sorted


【解决方案1】:

有三种方法可以进行合并:-

假设您将m listsn elements each 合并

算法1:-

一次合并两个列表。使用合并排序(如合并例程)在列表排序时合并。这很容易实现,无需任何库。但是如果 m 不大,O(m^2*n) 就足够小了。

算法2:-

这是对 1. 的改进,我们总是合并剩余列表中最小的两个列表。使用priority queue 执行此操作并选择最小的两个列表并将它们合并并将新列表添加到队列中。这样做直到只剩下 1 个列表,这将是您的答案。此技术用于huffman coding 并产生optimal merge pattern。这需要O(m*n*logm)。此外,对于类似大小的列表,可以将其设为parallel,因为我们可以选择一对列表并并行合并。假设你有m processors,那么算法可以理想地在O(n*logm)而不是O(m*n*logm)中运行

算法3:-

这是最有效的算法,您为所有列表的第一个元素维护priority queue,并提取 min 以获取新元素,同时维护列表 min 元素所属的索引,以便您可以添加该列表中的下一个元素。这需要O(s*logm),其中 s 是所有列表中的总元素。

【讨论】:

    【解决方案2】:

    假设

    以下方法适用于任何容器,如数组、向量、列表等。我假设我们正在使用列表。

    假设我们有要合并的m 排序列表。

    n 表示所有列表中元素的总数。

    想法

    结果列表中的第一个元素必须是所有列表头集合中的最小元素。

    这个想法很简单。只需选择最小的头部并将其从原始列表移动到结果中。您想在至少有一个非空列表时重复该例程。 这里的关键是快速选择最小的头部。

    如果 m 很小

    通过磁头的线性扫描O(m),得到O(m * n)的总时间,如果m是一个小常数,这很好。

    如果 m 不是那么小

    然后我们可以通过使用优先级队列(例如)做得更好。这里的不变量是堆中的最小元素始终是当前头中最小的元素。

    找到最小元素是堆是O(1),如果堆中有m元素,则删除最小是O(log m),向堆中插入一个元素也是O(log m)

    总之,对于每个n 元素,我们将其插入到堆中一次并从那里删除一次。堆的总复杂度为 O(n log m),如果 m 不是一个小常数,则比 O(n * m) 快得多。

    总结

    哪种方法更快取决于我们要合并多少个列表。如果m 很小,则选择线性扫描,在另一种情况下,使用优先队列 实现它。有时很难判断 m 是否很小,在这种情况下,一些实验会有所帮助。

    【讨论】:

      【解决方案3】:

      我假设没有库到merger。否则,您必须编写自己的linked list(这可能是转发,或normal list)。休息一样。简单示例(两个列表):

      #include <list>
      #include <iostream>
      
      using namespace std;
      
      int main(void)
       {
        list<int> a = { 1, 3, 5, 7, 9}, b = { 2, 4 , 6, 8, 9, 10}, c; //c is out
        for(auto it1 = begin(a), it2 = begin(b); it1 != end(a) || it2 != end(b);)
         if(it1 != end(a) && (it2 == end(b) || *it1 < *it2)) {
            c.push_back(*it1);
            ++it1;
          }
         else {
           c.push_back(*it2);
           ++it2;
          }
        for(auto x : c)
         cout<<x<<' ';
        cout<<'\n';
       }
      

      结果:

      1 2 3 4 5 6 7 8 9 9 10

      注意!您必须使用标志 -std=c++11(或其他 c++11)进行编译。例如:

      g++ -std=c++11 -Wall -pedantic -Wextra -O2 d.cpp -o program.out

      复杂度:Θ(n)

      内存:Θ(n)

      不难看出,每个元素在O(1)中只计算一次,我们有n个元素,所以它是Θ(n)。 p>

      内存复杂度显而易见。值得一提的是,如果不再需要这两个列表,无需额外分配(const memory)即可完成。

      算法本身已经described这么多次了,再写一遍也没意义。

      在主要问题中,我们有很多序列,但想法是一样的。这里有丰富的例子:

      int main(void)
       {
        vector<vector<int> > in{{ 1, 3, 5, 7, 9}, { 2, 4 , 6, 8, 9, 10}, {2,5,7,12,10,11,18}};
        vector<int> out;
        typedef tuple<int, vector<int>::iterator, vector<int>::iterator> element;
        priority_queue<element, vector<element>, greater<element> >  least;
        for(auto& x : in) //Adding iterators to the beginning of (no empty) lists
         if(!x.empty())   //and other parts of the element (value and end of vector)
          least.emplace(x.front(),begin(x),end(x));
        
        while(!least.empty()) {            //Solving
          auto temp = least.top(); least.pop();
          out.push_back(get<0>(temp));     //Add the smallest at the end of out
          ++get<1>(temp);
          if(get<1>(temp) != get<2>(temp)){//If this is not the end
            get<0>(temp) = *get<1>(temp);
            least.push(temp);              //Update queue
           }
         }
        
        for(const auto& x : out) //Print solution
         cout<<x<<' ';
        cout<<'\n';
       }
      

      复杂度:Θ(n log k)

      内存:Θ(n)

      pop和insert操作在O(log k)中,我们执行n次,所以是O(n log k)

      内存还是很明显的,我们在priority_queue中总是有k个元素,而O(n)在out sequence.

      【讨论】:

        【解决方案4】:

        此代码可能类似于基于指针和计数的合并排序,首先为每个序列创建一个指针和计数的“源”数组,然后分配第二个“目标”数组以合并“源”数组的指针和计数。该算法的每一遍都将基于“源”数组中的序列的指针对和计数合并到“目标”数组中,从而将数组中的条目数减少约 1/2。然后交换指向“源”和“目标”数组的指针,并重复合并过程,直到指针和计数数组只有一个条目。

        【讨论】:

          【解决方案5】:

          C++ 标准库包含std::merge

          std::vector<int> v1 { 1,2,5,7 }, 
                           v2 { 3,6,9 }, 
                           out;
          
          std::merge(v1.begin(), v1.end(), 
                     v2.begin(), v2.end(), 
                     std::back_inserter(out));
          

          http://en.cppreference.com/w/cpp/algorithm/merge

          【讨论】:

          • 感谢您的链接,但我正在寻找一种不使用任何库的算法
          • @user2970210 为什么? std::merge 是标准库的一部分。你没有使用标准库的任何元素吗?那么 cout、vector、printf 呢?很少有充分的理由不使用它。
          • 这是我正在做的问题的要求的一部分
          猜你喜欢
          • 2017-05-30
          • 2017-09-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-11-10
          • 2013-10-18
          相关资源
          最近更新 更多