Writing a merge method for lists
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' 开发者_如何学Pythonthis or plan for it, as I dont even know where to start.
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);
}
精彩评论