Linked Lists

Lesson 11

Last Week

  • dynamic array (Intlist or Int Container and Polygon or Point Container)

This week

  • Scanner

  • LinkedList exercises

  • Doubly linked list (moves forwards and backwards)

Scanner

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int my_int = scan.nextInt();
        double my_double = scan.nextDouble();
        String my_str = scan.next();

        System.out.println("int:" + my_int);
        System.out.println("double:" + my_double);
        System.out.println("string:" + my_str);

        scan.close();
    }
}
10
3.1415
hello world
int: 10
double: 3.1415
string: hello

LinkedList

advantages of LinkedList

  • removal of element in O(n) i.e. very fast

  • not contiguous in memory, so can expand

disadvantage of LinkedList

  • not contiguous in memory, so its slower to get each node

Basic implementation

class Node {
    int data;
    Node next;

    Node(int d) {
        data = d;
        next = null;
    }

    public String toString() {
        return data + "";
    }
}

class LinkedList {
    Node head;

    LinkedList() { head = null; }

    public LinkedList add(int elem) {
        if (head==null)
            head = new Node(elem);
        else{
            Node t = head; //transverse
            while (t.next != null)
                t = t.next;
            Node node = new Node(elem);
            t.next = node;
        }
        return this;
    }

    @Override
    public String toString() {
        Node node = head;
        StringBuilder str = new StringBuilder();

        while (node!=null){
            str.append(node + (node.next!=null ? " ," : ""));
            node = node.next;
        }

        return "LinkedList{" + str +'}';
    }
}

public class Main {
    public static void main(String[] args) {
        LinkedList ll = new LinkedList();
        System.out.println(ll);

        ll.add(10).add(7).add(3).add(9);
        System.out.println(ll);
    }
}
LinkedList{}
LinkedList{10 ,7 ,3 ,9}

Test Exercise: Add to an ordered Linked List

Solution:

class Main {
    public static void main(String[] args) {
        LinkedList ll = new LinkedList();
        System.out.println(ll);
        ll.add(1).add(2).add(4);

        ll.add_inorder(3);
        System.out.println(ll);


        ll.add_inorder(5);
        System.out.println(ll);

        ll.add_inorder(0);
        System.out.println(ll);
    }
}
LinkedList{1 ,2 ,3 ,4}
LinkedList{1 ,2 ,3 ,4 ,5}
LinkedList{0 ,1 ,2 ,3 ,4 ,5}

Exercise: Find the middle element (position) of LinkedList

  • DIY: - naïve solution

  • Our solution: Find the middle element (position) of LinkedList. You may only iterate through the linkedlist 1 time i.e. O(n)

Solution

The solution is similar to Floyd's cycle finding algorithm

Traverse linked list using two pointers.

  • Move one pointer by 1

  • Move other pointers by 2

When the fast pointer reaches the end slow pointer will reach the middle of the linked list.

class LinkedList {
    Node head;

    ...

    int middle_elem() {
        Node slow_ptr = head;
        Node fast_ptr = head;

        if (head != null) {
            while (fast_ptr != null && fast_ptr.next != null) {
                fast_ptr = fast_ptr.next.next;
                slow_ptr = slow_ptr.next;
            }
            return slow_ptr.data;
        }
        return -1; //if null
    }
public class Main {
    public static void main(String[] args) {
        LinkedList ll = new LinkedList();

        ll.add(1).add(2).add(3).add(4).add(5);
        System.out.println(ll);
        System.out.println(ll.middle_elem());
    }
}
LinkedList{1 ,2 ,3 ,4 ,5}
3

Test Exercise: Cycle in LinkedList

Detect if there is cycle in LinkedList

Hint! you should use the code we use before

I have added a print function to show the loop

class LinkedList {
    Node head;
    // ..... code from before

    public void print(){
        Node node = head;

        while (node!=null){
            System.out.print(node + " ");
            node = node.next;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        LinkedList ll = new LinkedList();

        ll.add(1).add(2).add(3).add(4);
        System.out.println(ll);

        //add cycle
        Node temp = ll.head.next;
        ll.head.next.next.next.next = temp; // 4 -> 2, 4 connected to 2

//        System.out.println(ll); //runs out of heap
        ll.print();

    }
}
1 2 3 4 2 3 4 2 3 4 2 3 4 2 3 4 2 3 4 2 3 4 2 3 4 2 3 4 2 3 4 2 3 4 2 3 4 2 3 4 2 3 4 2 3 4 2 3 4 2 ...

Solution

    void detect_cycle() {
        Node slow_ptr = head;
        Node fast_ptr = head;


        while (slow_ptr != null && fast_ptr != null && fast_ptr.next != null) {
            slow_ptr = slow_ptr.next; //increment by 1
            fast_ptr = fast_ptr.next.next; //increment by 2
            if (slow_ptr == fast_ptr) {
                System.out.println("there is a cycle starting from: " + slow_ptr);
                return;
            }
        }
        System.out.println("no cycle");
    }
public class Main {
    public static void main(String[] args) {
        LinkedList ll = new LinkedList();

        ll.add(1).add(2).add(3).add(4);
        System.out.println(ll);

        //add cycle
        Node temp = ll.head.next;
        ll.head.next.next.next.next = temp; // 4 -> 2, 4 connected to 2


        ll.detect_cycle();
    }
}
LinkedList{1 ,2 ,3 ,4}
there is a cycle starting from: 4

Note: since if the linked list is empty the code will work since in

while (slow_ptr != null && fast_ptr != null && fast_ptr.next != null)

slow_ptr will equal to null and exit the while before checking fast_ptr.next

But how do we remove the the cycle?

Easy just add 1 line of code

slow_ptr.next = null;

so now

void detect_and_remove_cycle() {
        Node slow_ptr = head;
        Node fast_ptr = head;


        while (slow_ptr != null && fast_ptr != null && fast_ptr.next != null) {
            slow_ptr = slow_ptr.next; //increment by 1
            fast_ptr = fast_ptr.next.next; //increment by 2
            if (slow_ptr == fast_ptr) {
                System.out.println("there is a cycle starting from: " + slow_ptr);
                slow_ptr.next = null; // <========= added this
                return;
            }
        }
        System.out.println("no cycle");
    }
public class Main {
    public static void main(String[] args) {
        LinkedList ll = new LinkedList();

        ll.add(1).add(2).add(3).add(4);
        System.out.println(ll);

        //add cycle
        Node temp = ll.head.next;
        ll.head.next.next.next.next = temp; // 4 -> 2, 4 connected to 2


        ll.detect_and_remove_cycle();
        System.out.println(ll);
    }
}
LinkedList{1 ,2 ,3 ,4}
there is a cycle starting from: 4
LinkedList{1 ,2 ,3 ,4}

Exercise: Reverse LinkedList - Iteratively- DIY (15 minutes)

Solution

  LinkedList reverse() {
        Node prev = null;
        Node current = head;
        Node next = head;

        while (current != null) {
            next = current.next;
            current.next = prev;
            prev = current;
            current = next;
        }

        head = prev;
        return this;
    }
public class Main {
    public static void main(String[] args) {
        LinkedList ll = new LinkedList();

        ll.add(1).add(2).add(3).add(4);
        System.out.println(ll);
        System.out.println(ll.reverse());
    }
}
LinkedList{1 ,2 ,3 ,4}
LinkedList{4 ,3 ,2 ,1}

Exercise: Reverse LinkedList - Recursively

solution

    LinkedList reverseRec(){
        head = reverse_rec(head);
        return this;
    }

    Node reverse_rec(Node head) {
        //Base Case 1:
        if(head == null) 
            return head;

        //Base Case 2: last node or only one node
        if(head.next == null)
            return head;

        Node newHeadNode = reverse_rec(head.next);

        // change references for middle chain
        head.next.next = head;
        head.next = null;

        // send back new head node in every recursion
        return newHeadNode;
    }
public class Main {
    public static void main(String[] args) {
        LinkedList ll = new LinkedList();

        ll.add(1).add(2).add(3).add(4);
        System.out.println(ll);
        System.out.println(ll.reverseRec());
    }
}
LinkedList{1 ,2 ,3 ,4}
LinkedList{4 ,3 ,2 ,1}

Exercise: Partition LinkedList

all even numbers appear before all the odd numbers in the modified linked list

Solution

  1. Pointer should be at last node (iterate to get there)

  2. Move all the odd nodes to the end

    • Consider all odd nodes before the first even node and move them to end.

    • Change the head pointer to point to the first even node.

    • Consider all odd nodes after the first even node and move them to the end.

    LinkedList partition_evens_odds() {
        Node end = head;
        Node prev = null;
        Node curr = head;

        // Get pointer to last Node
        while (end.next != null)
            end = end.next;

        Node new_end = end;

        // Consider all odd nodes before getting first even node
        while (curr.data %2 !=0 && curr != end) {
            new_end.next = curr;
            curr = curr.next;
            new_end.next.next = null;
            new_end = new_end.next;
        }

        // do following steps only if there is an even node
        if (curr.data %2 ==0) {
            head = curr;

            // now curr points to first even node
            while (curr != end) {
                if (curr.data % 2 == 0) {
                    prev = curr;
                    curr = curr.next;
                }
                else {
                    prev.next = curr.next;  // Break the link between prev and curr
                    curr.next = null; // Make next of curr as null
                    new_end.next = curr; //Move curr to end
                    new_end = curr; //Make curr as new end of list
                    curr = prev.next; //Update curr pointer
                }
            }
        } else { // We have to set prev before executing rest of this code
            prev = curr;
        }

        if (new_end != end && end.data %2 != 0) {
            prev.next = end.next;
            end.next = null;
            new_end.next = end;
        }
        return this;
    }
public class Main {
    public static void main(String[] args) {
        LinkedList ll = new LinkedList();

        ll.add(1).add(2).add(3).add(4).add(5).add(6).add(7);
        System.out.println(ll);

        System.out.println(ll.partition_evens_odds());
    }
}
LinkedList{1 ,2 ,3 ,4 ,5 ,6 ,7}
LinkedList{2 ,4 ,6 ,1 ,3 ,5 ,7}

Doubly LinkedList

Advantages over singly linked list

  • can traverse in both forward and backward direction

  • and more, so read up

Disadvantages over singly linked list

  • takes up more space

  • All operations require an extra pointer previous to be maintained. For example, in insertion, we need to modify previous pointers together with next pointers.

Last updated