【问题标题】:Merging 8 sorted lists in c++, which algorithm should I use在 C++ 中合并 8 个排序列表,我应该使用哪种算法
【发布时间】:2010-10-27 12:51:56
【问题描述】:

我有 8 个排序列表需要合并到 1 个排序列表中。我不知道做到这一点的最佳方法。我在想以下几点:

void merge_lists_inplace(list<int>& l1, const list<int>& l2)
{
    list<int>::iterator end_it = l1.end();
    --end_it;
    copy(l2.begin(), l2.end(), back_inserter(l1));
    ++end_it;
    inplace_merge(l1.begin(), end_it, l1.end());
}

list<int> merge_8_lists(list<int>[8] lists)
{
    merge_lists_inplace(lists[0], lists[1]);
    merge_lists_inplace(lists[2], lists[3]);
    merge_lists_inplace(lists[4], lists[5]);
    merge_lists_inplace(lists[6], lists[7]);

    merge_lists_inplace(lists[0], lists[2]);
    merge_lists_inplace(lists[4], lists[6]);

    merge_lists_inplace(lists[0], lists[4]);

    return lists[0];
}

但是最后担心排序会更好吗?

list<int> merge_8_lists(list<int>[8] lists)
{
    for (int i = 1; i < 8; ++i)
        copy(lists[i].begin(), lists[i].end(), back_inserter(lists[0]));        
    lists[0].sort();
    return lists[0];
}

旁注:我不在乎列表是否被修改。

【问题讨论】:

    标签: c++ sorting stl merge


    【解决方案1】:

    使用priority queue(例如heap )。伪代码:

    Let P = a priority queue of the sorted lists, sorted by the smallest element in each list
    Let O = an empty output list
    While P is not empty:
      Let L = remove the minimum element from P
      Remove the first element from L and add it to O
      If L is not empty, add L to P
    

    还有一个简单的(未经测试的!)C++ 中的具体实现:

    #include <list>
    #include <set>
    
    template<typename T>
    struct cmp_list {
        bool operator()(const std::list<T> *a, const std::list<T> *b) const {
            return a->front() < b->front();
        }
    };
    
    template<typename T>
    void merge_sorted_lists(std::list<T> &output, std::list<std::list<T> > &input)
    {
        // Use a std::set as our priority queue. This has the same complexity analysis as
        // a heap, but has a higher constant factor.
        // Implementing a min-heap is left as an exercise for the reader,
        // as is a non-mutating version
        std::set<std::list<T> *, cmp_list<T> > pq;
    
        for ( typename std::list<std::list<T> >::iterator it = input.begin();
                it != input.end(); it++)
        {
            if (it->empty())
                continue;
            pq.insert(&*it);
        }
    
        while (!pq.empty()) {
            std::list<T> *p = *pq.begin();
            pq.erase(pq.begin());
    
            output.push_back(p->front());
            p->pop_front();
    
            if (!p->empty())
                pq.insert(p);
        }
    }
    

    【讨论】:

    • 为什么是 std::set 而不是 std::priority_queue?
    • 不知道这是标准的。 :)
    • 你的意思好像是"std::list &output",merge_sorted_lists的第一个参数。
    【解决方案2】:

    您可以尝试将合并排序一次应用于每个列表:

    http://en.wikipedia.org/wiki/Merge_sort

    这有合并排序的算法。本质上,您将使用列表 1 和 2 并对它们进行合并排序。然后,您将获取新的组合列表并使用列表 3 进行排序,并继续进行,直到您拥有一个完全排序的列表。

    编辑:

    实际上,因为您的列表已经排序,所以只需要合并排序的最后一部分。我会迭代地将列表组合成越来越大的部分,同时对每个更大的列表进行排序,直到获得完整列表,这基本上是合并排序在完成分而治之之后所做的。

    【讨论】:

    • 请注意,这是 O(nm) 时间,比我在另一个答案中提出的 O(n lg m) 时间算法差
    【解决方案3】:

    如果性能不是问题,我会最后对列表进行排序。该代码更具可读性、更短,并且不太可能被将来重新访问该代码的人搞砸。

    【讨论】:

      【解决方案4】:

      这是一个标准的(虽然是 8 路)归并排序。

      基本上,您“打开”八个排序列表,然后开始处理它们,每次都提取最小值,例如:

      # Open all lists.
      
      open newlist for write
      for i = 1 to 8:
          open list(i) for read
      end for
      

       

      # Process forever (break inside loop).
      
      while true:
          # Indicate that there's no lowest value.
      
          smallidx = -1
      
          # Find lowest value input list.
      
          for i = 1 to 8:
              # Ignore lists that are empty.
      
              if not list(i).empty:
                  # Choose this input list if it's the first or smaller
                  #  than the current smallest.
      
                  if smallidx = 1:
                      smallidx = i
                      smallval = list(i).peek()
                  else:
                      if list(i).peek() < smallval:
                          smallidx = i
                          smallval = list(i).peek()
                      end if
                  end if
              end if
          end for
      

       

          # No values left means stop processing.
      
          if smallidx = -1:
              exit while
          end if
      
          # Transfer smallest value then continue.
      
          smallval = list(smallidx).get()
          newlist.put(smallval)
      end while
      

      【讨论】:

        【解决方案5】:

        基本上你正在做多路归并排序的一部分,除了你的东西已经排序......

        http://lcm.csa.iisc.ernet.in/dsa/node211.html

        只需在每个数组中找到最低的(几乎用作堆栈)并将其放入输出中,直到所有堆栈都为空...

        【讨论】:

          【解决方案6】:

          你想要a merge sort。您的列表已经拆分,但并未一直拆分到最小级别。您可能想要这样做:

          unsorted_list = concatenate(list[0], list[1], list[2], ... , list[7]);
          sorted_list = merge_sort(unsorted_list);
          

          这不应该是一个耗费时间/内存的操作,因为连接应该添加从列表中最后一个节点到下一个列表第一个元素的链接。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-04-03
            • 2014-04-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-11-14
            相关资源
            最近更新 更多