Flatten a Multi-Level Linked List

  • Flatten the multi-level linked list into a single-level linked list by linking the end of each level to the start of the next one.
  • Each node has a next pointer and child pointer.
  • The order of the nodes on each level needs to be preserved.
  • All the nodes in one level must connect before appending nodes from the next level.
  • BFS => Queue => Linear space complexity
  • Can we flatten in place ?
    • We can append each child linked list to the end of the current level, which effectively merges these two levels into one.
    • With all the nodes on level ‘L + 1’ appended to level ‘L’, we can continue this process by appending nodes from level ‘L + 2’ to level ‘L + 1’, and so on.

The point:

  • use 2 pointers
  • tail points to the end of the level 0 list
  • current traverse the list. Each time a child is found, the child list is appended to the end of the level 0 (including the child’s children)
  • tail pointer : we would need a reference to level 1’s tail node so we can easily add nodes to the end of the linked list.
  • curr pointer : traverse the linked list. Whenever it encounters a node with a child node that isn’t null, the child linked list is added to the end of the linked list. tail pointer must be updated

Complexity : | Time | Space | |——|——-| | O(n) | O(1) |

About Rust :

  • We have pointers pointing to the same structure => Rc
  • We need to be able to modify what’s is pointed => RefCell
    • RefCell supports interior mutability. This is a way of bypassing Rust’s borrowing rules at runtime rather than at compile-time.
    • Without RefCell, an Rc cannot be modified. In fact, Rust forbids us to modify a structure contained in an Rc, as Rc only gives (immutable) read access to the underlying data.
  • Pay attention to :
    let linked_lists: Vec<Link> = descriptor
          .iter()
          .map(|(_, vals)| build_the_list(vals.clone()))
          .collect();
    
  • YES : tested on the Rust Playground

TODO :

  • More comments in the source code
use std::cell::RefCell;
use std::rc::Rc;

type Link = Option<Rc<RefCell<MultiLevelListNode>>>;    // type alias. Use Option and Rc<RefCell<...>>) to allow optional pointers to the next and child nodes 
                                                        // Rc<RefCell<...>>) to handle shared and mutable references

#[derive(Debug)]
struct MultiLevelListNode {
    val: i32,
    next: Link,
    child: Link,
}

impl MultiLevelListNode {
    fn new(val: i32, next: Link, child: Link) -> Link {
        Some(Rc::new(RefCell::new(Self { val, next, child })))
    }
}

fn print_linked_list(mut head: Link) {
    while let Some(node) = head {
        let node_borrow = node.borrow();
        print!("{}->", node_borrow.val);
        head = node_borrow.next.clone();
    }
    println!("EOL"); // Enf Of List
}

// Build a linked list from a vector 
fn build_the_list(values: Vec<i32>) -> Link {
    let mut head: Link = None;
    for &val in values.iter().rev() {
        head = MultiLevelListNode::new(val, head, None);
    }
    head
}

// Attach a child list to the node with matching value in the parent list
fn attach_list(ll_child: Link, val: i32, ll_parent: &Link) {
    let mut curr = ll_parent.clone();
    while let Some(node) = curr {
        if node.borrow().val == val {
            node.borrow_mut().child = ll_child;
            break;
        }
        curr = node.borrow().next.clone();
    }
}

// Flatten the multilevel list by appending child lists at the end
fn flatten_multi_level_list(head: Link) -> Link {
    if head.is_none() {
        return None;
    }

    let mut tail = head.clone();
    while let Some(t) = tail.clone() {
        let next = t.borrow().next.clone();  // Borrow ends here
        if next.is_some() {
            tail = next;
        } else {
            break;
        }
    }

    let mut curr = head.clone();
    while let Some(node) = curr {
        let mut node_mut = node.borrow_mut();
        if let Some(child) = node_mut.child.take() {
            // Append the child list to the tail
            tail.as_ref().unwrap().borrow_mut().next = Some(child.clone());

            // Move tail to the new end
            let mut new_tail = tail.clone();
            while let Some(t) = new_tail.clone() {
                let next = t.borrow().next.clone();  // Borrow ends here
                if next.is_some() {
                    new_tail = next;
                } else {
                    break;
                }
            }
            tail = new_tail;
        }
        curr = node_mut.next.clone();
    }
    head
}

fn main(){     // no main() if this code runs in a Jupyter cell 
// {                 // local scope to avoid issue with the lifetime of head during borrow
    let descriptor = [
        (None, vec![1, 2, 3, 4, 5]),
        (Some(2), vec![6, 7]),
        (Some(7), vec![10]),
        (Some(4), vec![8, 9]),
        (Some(8), vec![11]),
    ];

    let linked_lists: Vec<Link> = descriptor
        .iter()
        .map(|(_, vals)| build_the_list(vals.clone()))
        .collect();

    attach_list(linked_lists[1].clone(), 2, &linked_lists[0]);
    attach_list(linked_lists[2].clone(), 7, &linked_lists[1]);
    attach_list(linked_lists[3].clone(), 4, &linked_lists[0]);
    attach_list(linked_lists[4].clone(), 8, &linked_lists[3]);

    let flat = flatten_multi_level_list(linked_lists[0].clone());
    print_linked_list(flat); // 1->2->3->4->5->6->7->8->9->10->11->EOL
} // end of local scope OR end of main()    
1->2->3->4->5->6->7->8->9->10->11->EOL





()

Back to top

Published on: Jun 22 2025 at 09:00 AM | Last updated: Jun 22 2025 at 09:00 AM

Copyright © 1964-2025 - 40tude