Writing a merge method for lists

荒凉一梦 提交于 2019-12-06 15:24:11

Create a third, empty list (called the "result list").

Loop while there are still elements left in one of the lists:

  • If the first list is not empty, pop an element off the front of the first list and push it onto the back of the result list.
  • If the second list is not empty, pop an element off the front of the second list and push it onto the back of the result list.

It would look something like this with the C++ std::list container (this algorithm is slightly different, since it's a bit easier to handle "extra" elements outside of the main loop):

template <typename T>
std::list<T> merge_lists(const std::list<T>& a, const std::list<T>& b)
{
    std::list<T> r;
    while (!a.empty() && !b.empty())
    {
        r.splice(r.end(), a, a.begin());
        r.splice(r.end(), b, b.begin());
    }

    r.splice(r.end(), a, a.begin(), a.end());
    r.splice(r.end(), b, b.begin(), b.end());

    return r;
}

In you don't want to change the input lists then you can copy the first list and then insert into the new list elements from the second list in every other position.

#include <iostream>
#include <list>
#include <iterator>

int main(int argc, char* argv[])
{
    typedef std::list<int> list;
    list a;  // and fill it ...
    list b;  // and fill that too ...

    list c(a);
    for( list::iterator p(++c.begin()), i(b.begin()); i != b.end(); ++i ) {
        p = ++ ++ c.insert(p, *i);
    }
    return 0;
}

Also of interest is the Boost Zip Iterator. It allows you to iterate over two or more containers at the same time. So you can create an empty list, iterate over the two input lists in parallel and insert one element from each into the new list.

Take the following steps, going down each line, with arrows representing moves. For example, after the first step, the 0 had been removed from A and final only has one item:

List:  A      final      B
       0  -->   0
                4   <--  4
       1  -->   1
                5   <--  5
       2  -->   2
                6   <--  6
       3  -->   3

An example with std::list:

std::list<int> A, B;
A.push_back(0); A.push_back(1); A.push_back(2); A.push_back(3);
B.push_back(4); A.push_back(5); A.push_back(6);

std::list<int> final;
for (std::list<int>::iterator a, b;
     (a = A.begin()) != A.end() and (b = B.begin()) != B.end();)
{
  final.splice(final.end(), A, a);
  final.splice(final.end(), B, b);
}
for (std::list<int>::iterator x; (x = A.begin()) != A.end();) {
  final.splice(final.end(), A, x);
}
for (std::list<int>::iterator x; (x = B.begin()) != B.end();) {
  final.splice(final.end(), B, x);
}

Extracting the splice cleans things up a tiny bit, but, more importantly, you should be able to write the below move_back for your (apparently custom) list type, then using it is identical to std::list:

// Moves pos from source into dest; returns what was the position after
// pos in the source list.
template<class List>
typename List::iterator move_back(List &dest, List &source,
                                              typename List::iterator pos)
{
  typename List::iterator next = pos;
  ++next;
  dest.splice(dest.end(), source, pos);
  return next;
}
template<class List>
void move_back(List &dest, List &source) {
  dest.splice(dest.end(), source, source.begin(), source.end());
}

void example() {
  std::list<int> A, B;
  A.push_back(0); A.push_back(1); A.push_back(2); A.push_back(3);
  B.push_back(4); A.push_back(5); A.push_back(6);

  std::list<int> final;
  for (std::list<int>::iterator a = A.begin(), b = B.end();
       a != A.end() and b != B.end();)
  {
    a = move_back(final, A, a);
    b = move_back(final, B, b);
  }
  move_back(final, A);
  move_back(final, B);
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!