Writing a merge method for lists

南笙酒味 提交于 2019-12-10 10:43:36

问题


I'm trying to write a method that merges a doubly linked list in an alternating fashion. So if we have two int lists (0,1,2,3) and (4,5,6), we'd just have one final list of (0,4,1,5,2,6,3). Each list has a head, a tail, a next, and a prev pointer.

Its hurting my mind to try to figure out where to start or how this would work. I've tried to trace it on paper, but no progress.

Can anyone guide me in the right direction? Whats a good way to 'picture' this or plan for it, as I dont even know where to start.


回答1:


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



回答2:


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.




回答3:


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


来源:https://stackoverflow.com/questions/4998627/writing-a-merge-method-for-lists

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!