简体   繁体   中英

How do I recursively split a linked list of integers into an odd and even list in C++?

Hey I just took a quiz in school involving linked lists with C++ and I couldn't figure out how to get the function to work in the way my prof wanted. The quiz is over but I'd like to solve it anyways and try to understand linked lists better. I've been trying to figure this out for like 3 hours so, help would be appreciated.

odd_even needed to have this signature and needed to be a recursive solution. h is the original linked list passed in and nodes are split into the odd and even lists. odd and even are initially set to nullptr and odd_even sets the original list h to nullptr after it's completed the split. He also said it was to be a deconstructive solution, no creating new lists or nodes. I'm not 100% clear on that but I took it to mean I should only manipulate/reassign existing nodes.

I've tried many things, but this was what I submitted:

node* odd_even(node* p, node*& odd, node*& even) {
    if (!p) {
        return nullptr;
    }
    else if (p->data % 2 == 0) {
        even = p;
        even->next = odd_even(p->next, odd, even->next);
    }
    else {
        odd = p;
        odd->next = odd_even(p->next, odd->next, even);
    }
    return p;
}
int main() {
    node* odd = nullptr;
    node* even = nullptr;
    node* h = new node{ 1, new node{3, new node{5, new node{6, new node{8, new node{9, nullptr} } } } } };
    printr(h);
    h = odd_even(h, odd, even);
    printr(h);
    printr(odd);
    printr(even);

}
Expected Input:
node* h: 1->3->5->6->8->9->nullptr
node* odd, even: nullptr.

Expected Output:
h: 1->3->5->6->8->9->nullptr
h = odd_even(h, odd, even)
h: nullptr
even: 6->8->nullptr
odd: 1->3->5->9->nullptr

I tried finding the next node for each list through tail recursive calls, but that breaks down when it switches from odd to even assignment call or vice versa. I tried making my recursive calls up to the end of the list then split them up on the way back, but then grabbing the first node was difficult and I don't think thats the solution anyways. I also had issues getting the whole thing to ultimately return a nullptr... I just don't think I'm approaching it right and I could use a bit of guidance here. Thanks!


Update: I spent another few hours on it and came up with 2 solutions. 1 is better than the other pretty sure. Thanks for the feedback guys, I'm happy I did figure it out by myself a bit first. I look forward to reading the comments, I'm sure theres better ways to go about it than I have.

My first solution (concise but doesn't return nullptr for the original list at the end...):

node* odd_even(node* p, node*& odd, node*& even) {
    if (!p) {
        return nullptr;
    }
    p->next = odd_even(p->next, odd, even);
    if (p->data % 2 == 0 ) {
        p->next = even;
        even = p;
    }else {
        p->next = odd;
        odd = p;
    }
    return p;
}

My second solution (a bit of a mess but fulfills all the requirments):

node* odd_even(node* p, node*& odd, node*& even) {
        if (!p) {
            return nullptr;
        }
        if (p->data % 2 == 0) {
            even = p;
        }else {
            odd = p;
        }
        if (p->next) {
            if (p->next->data % 2 == 0) {
                if (even) {
                    even->next = odd_even(p->next, odd, even->next);
                }else {
                    even = odd_even(p->next, odd, even);
                }
            }else {
                if (odd) {
                    odd->next = odd_even(p->next, odd->next, even);
                }else {
                    odd = odd_even(p->next, odd, even);
                }
            }
            return p;
        }
        even->next = odd_even(p->next, odd, even);
        odd->next = odd_even(p->next, odd, even);
}

odd_even() created two new lists by rewiring one old list.

So far, so good.

Unfortunately, while you put all the nodes from the old list where they belong, you forgot terminating them by setting the final next-pointer to null. Thus, one will have the original last node and is thus terminated, while the other links to some node from the other list.

Where do you terminate? Well, just before returning null.

Also, you should either return null directly, or by tail-recursion.

node* odd_even(node* p, node*& odd, node*& even) noexcept {
    if (!p) {
        odd = even = nullptr;
        return nullptr;
    } else if (p->data % 2) {
        odd = p;
        return odd_even(p->next, p->next, even);
    } else {
        even = p;
        return odd_even(p->next, odd, p->next);
    }
}

By the way, this is a terrible demonstration for recursion, as the iterative version is even simpler, and doesn't depend on the compiler to eliminate recursive calls, which it should do here as a matter of quality of implementation .

node* odd_even(node* p, node*& odd, node*& even) noexcept {
    auto p_odd = &odd, p_even = &even;
    for (; p; p = p->next) {
        auto& chosen = p->data % 2 ? p_odd : p_even;
        *chosen = p;
        chosen = &p->next;
    }
    *p_odd = *p_even = nullptr;
    return nullptr;
}

Consider the even case (odd has the same problem)

even = p;
even->next = odd_even(p->next, odd, even->next);

You have the same value in p and even , so they have the same ->next

You need to unlink p from the list to process it. I don't know why your instructor wants odd_even to return a node * , as it should always return a null pointer.

even = p;
node * next = p->next;
p->next = nullptr;
odd_even(next, odd, p->next);

You can simplify that with std::exchange

even = p;
odd_even(std::exchange(p->next, nullptr), odd, p->next);

See it live

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM