Search code examples
pythonalgorithmfunctionlinked-list

Linked Lists: How to remove odd numbers?


I am currently taking an online computer science introductory course and have just learned the concept of a linked list. Though I understand the concept of linked lists, I still am unsure as how to deal with linked lists.

As such, I seek out help in solving the following problem, which will be of significant help for my understanding of linked lists:

Write a function (not in LinkedList class definition) that given a linked list, will change that linked list to filter out odd numbers. Immediately after the function returns, the linked list will only have even numbers.

I am unsure as to how to access the nodes in the list and check whether they are odd or even and remove or keep them accordingly.

I apologize if this seems like a trivial question, but I would appreciate any help that might help me learn.

The code for the linked list and node classes (as provided by the online course):

class Node:
    def __init__(self, data=None, next_node=None):
        self.data = data
        self.next = next_node

    def __str__(self):
        return str(self.data)


class LinkedList:
    def __init__(self):
        self.length = 0
        self.head = None
    
    def print_list(self):
        node = self.head
        while node is not None:
            print(node, end=' ')
            node = node.next
        print('')
    
    def add_at_head(self, node):
        node.next = self.head
        self.head = node
        self.length += 1
    
    def remove_node_after(self, node):
        if node.next is not None:
            temp = node.next
            node.next = node.next.next
            temp.next = None
            self.length -= 1
    
    def remove_first_node(self):
        if self.head is None:
            return
        temp = self.head
        self.head = self.head.next
        temp.next = None
        self.length -= 1
    
    def print_backward(self):
        def print_nodes_backward(node):
            if node.next is not None:
                print_nodes_backward(node.next)
            if node is not None:
                print(node, end=' ')
        
        if self.head is not None:
            print_nodes_backward(self.head)
        
        print('')

Solution

  • Let's say you have a bare-bones simple linked list that looks like this:

    class LinkedList:
        class ListNode:
            def __init__(self, data):
                self.data = data
                self.next = None
        def __init__(self):
            self.head = None
        def add(self, data):
            if self.head is None:
                self.head = LinkedList.ListNode(data)
            else:
                current_node = self.head
                while current_node.next is not None:
                    current_node = current_node.next
                current_node.next = LinkedList.ListNode(data)
        def __str__(self):
            ret = "["
            current_node = self.head
            while current_node is not None:
                ret = ret + str(current_node.data)
                if current_node.next is not None:
                    ret = ret + ", "
                current_node = current_node.next
            ret = ret + "]"
            return ret
    

    In other words, the LinkedList contains a single head, which is a ListNode. Every element in the Linked List is contained in a ListNode, and each ListNode points towards the next element in the list.

    As you can see, for adding an element to the list, we either create a node at the head if the list is empty (i.e. self.head is None), or we traverse to the end of the list by continuously jumping to the .next element for each ListNode, starting from the head. We also use this paradigm for printing a string representation of our list.

    So, to remove any node from the linked list, we can simply change the node that references it, so that the node we want to remove gets skipped. At which point it will disappear.

    To remove all list nodes containing odd-numbered data, we might do something like this:

        def remove_odds(self):
            # special case: head node
            # remove odd head elements by simply setting head to the next element after
            while (self.head is not None) and (self.head.data % 2 == 1):
                self.head = self.head.next
            # regular case: the rest of the nodes
            current_node = self.head
            while (current_node is not None) and (current_node.next is not None):
                # if the next node's data is odd, then
                if current_node.next.data % 2 == 1:
                    # skip that node by pointing this node's .next to the next node's .next
                    current_node.next = current_node.next.next
                # otherwise, move forwards in the list
                else:
                    current_node = current_node.next
    

    Proof of concept:

    >>> lst = LinkedList()
    >>> lst.add(2)
    >>> lst.add(5)
    >>> lst.add(6)
    >>> lst.add(3)
    >>> lst.add(7)
    >>> lst.add(8)
    >>> lst.add(10)
    >>> lst.add(1)
    >>> lst.add(4)
    >>> print(lst)
    [2, 5, 6, 3, 7, 8, 10, 1, 4]
    >>> lst.remove_odds()
    >>> print(lst)
    [2, 6, 8, 10, 4]