Palindromic Linked List

  • Given the head of a singly linked list, determine if it is a palindrome.
    • 1 -> 2 -> 2 -> 1 True
    • 1 -> 2 -> 3 -> 2 -> 1 True
    • 1 -> 2 -> 3 -> 4 False

The point:

  • We only need to compare the first half of the original linked list with the reverse of the second half
  • If there are an odd number of elements, we can include the middle node in both halves

So :

  • Find the middle of the linked list to get the head of the second half
  • Reverse the second half of the linked list from this middle node
  • We assume it is acceptable to modify the linked list (here second half is reversed)

Complexity Analysis :

Time Space
O(n) O(1)
  • n is the capacity of the cache
  • O(n) because iterate 3 times along the list
  • O(1) in space complexity

About Rust :

type Link = Option<Box<Node>>; // type alias. Use Option and Box to allow an optional pointer to the next node 

#[derive(Clone)]
struct Node {
    val: i32,
    next: Link, // use the alias here for clarity
}

impl Node {
    fn new(val: i32, next: Link) -> Self {
        Self { val, next }
    }
}

// It borrows the list instead of taking ownership
// Returns a Link (i.e. a cloned Box<Node>). Indeed we want to perform a destructive reverse_list on the second half only. 
// This cloning avoids altering the original list, which is may be important
fn find_middle(head: &Link) -> Link {
    let mut slow = head.as_ref();
    let mut fast = head.as_ref();

    while let Some(f_node) = fast {
        if let Some(f_next) = f_node.next.as_ref() {
            fast = f_next.next.as_ref();
            slow = match slow {
                Some(n) => n.next.as_ref(),
                None => None,
            };
            // The previous match can be replaced by the line below
            //      .and_then(...) apply an operation that returns an Option, if and only if the Option is Some(...), otherwise, it returns None.
            //      if slow is Some(val), then the closure |n| ... is executed 
            //      if slow is None, then nothing is executed, and .and_then(...) returns None
            // slow = slow.and_then(|n| n.next.as_ref());
        } else {
            break;
        }
    }
    slow.cloned() // Return a new Box<Node> if any
}


// see 50_linked_list_reversal.ipynb
// It takes ownership and return the new inverted head.
// The returned version replaces the old list, 
fn reverse_list(mut head: Link) -> Link {
    let mut prev_node = None; // Start with no previous node

    while let Some(mut current_node) = head {
        // Save the next node before changing pointers
        let next_node = current_node.next.take(); // take() replaces current_node.next with None and gives its original value

        // Reverse the pointer
        current_node.next = prev_node;

        // Move prev_node and head forward
        prev_node = Some(current_node);
        head = next_node;
    }
    prev_node
}

// Use a reference to the list
fn palindromic_linked_list(head: &Link) -> bool {
    let mid = find_middle(head);
    let second_head = reverse_list(mid);
    let mut ptr1 = head.as_ref();
    let mut ptr2 = second_head.as_ref();

    while let Some(node2) = ptr2 {
        if let Some(node1) = ptr1 {
            if node1.val != node2.val {
                return false;
            }
            ptr1 = node1.next.as_ref();
            ptr2 = node2.next.as_ref();
        } else {
            return false;
        }
    }
    true
}

// 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 mut head_a = None; // Start with an empty list (head is None)
    let vals_a = vec![1, 2, 3, 2, 1];
    for v in vals_a.into_iter().rev() {
        head_a = Some(Box::new(Node::new(v, head_a)));
    }
    println!("{}", palindromic_linked_list(&head_a));  // true
    
    let mut head_b = None; // Start with an empty list (head is None)
    let vals_b = [1, 2, 1, 2];
    for v in vals_b.into_iter().rev() {
        head_b = Some(Box::new(Node::new(v, head_b)));
    }    
    println!("{}", palindromic_linked_list(&head_b));  // false

} // end of local scope OR end of main()    
true
false





()

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