Linked List

CSCI 1913 – Introduction to Algorithms, Data Structures, and Program Development
Adriana Picoral

Array-based List

Array-based List

Elements are stored in contiguous memory (issue of allocation)

Front of List Middle of List End of List
get O(1) O(1) O(1)
set O(1) O(1) O(1)
insert O(n) O(n) O(1)
remove O(n) O(n) O(1)
  • random access (access through an index) is fast
  • insert/remove something – shift everything else

Linked lists

A linked list is a chain of nodes, where each node contains:

  • data - the actual value you’re storing (like arrays)
  • a pointer/reference - the address of the next node in the chain

No need for contiguous memory allocation (nodes can be scattered anywhere in memory)

Linked List

  • dynamic structure, can grow and shrink as needed (no allocation needed)
  • elements are arranged in a linear sequence
  • implemented through nodes – each node has a reference to the next node

Nodes

To create a linked list:

  • a class of self-referential objects
  • each object of the class contains a reference to an object of the same class
  • convention is to name the class Node

Write a class in Java called Node that has two instance variables: data (any type), and a reference to another node.

Write the constructor method, and a set and get method for each instance variable.

Solution

public class Node<T> {
    private T data;
    private Node<T> next;

    // constructor
    public Node(T data) {
        this.data = data;
        next = null;
    }

    // setters
    public void setNext(Node<T> next) {
        this.next = next;
    }

    public void setData(T data) {
        this.data = data;
    }

    // getters
    public Node<T> getNext() {
        return next;
    }

    public T getData() {
        return data;
    }

}

LinkedList class

  • What instance variables do you need?

LinkedList class

A linked list has a head (reference to a Node)

LinkedList set up and constructor

public class LinkedList<T> {
    private Node<T> head;

    // constructor
    public LinkedList() {
        head = null;
    }

}

Methods

  • What methods do you need to implement?

Here’s our generic interface we used to implement our ArrayList:

public interface List<T> {
    void append(T value);
    void prepend(T value);
    void insert(int index, T value);
    int length();
    T get(int index);
    void set(int index, T value);
    T pop(int index);
}

Implement append, prepend and length in your LinkedList<T> class + a toString() method to test your code. Submit your List.java, Node.java and LinkedList.java files to gradescope.

prepend – O(1)

public void prepend(T value) {
  Node<T> nodeToAdd = new Node<>(value);
  if (head == null) {
    head = nodeToAdd;
    tail = nodeToAdd;
  } else {
    head.setNext(nodeToAdd);
    head = nodeToAdd;
  }
  length++;
}

append – O(n)

public void append(T value) {
  Node<T> nodeToAdd = new Node<T>(value)
  if (head == null) head = nodeToAdd;
  else {
    Node<T> currNode = head;
    while (currNode.getNext() != null) {
      currNode = currNode.getNext();
    }
    currNode.setNext(nodeToAdd);
  }
  length++;
}

append – O(1)

Keep a tail instance variable.

public void append(T value) {
  Node<T> nodeToAdd = new Node<>(value);
  if (head == null) {
    head = nodeToAdd;
    tail = nodeToAdd;
  } else {
    tail.setNext(nodeToAdd);
    tail = nodeToAdd;
  }
  
};

toString() method

Let’s create a toString() method to test our list insertion.

Write a toString() method that traverses the entire list

  • Concatenate values

toString() solution

@Override
public String toString() {
  String result = "";
  Node<T> currNode = head;
  while (currNode != null) {
    result += currNode.getData() + " ";
    currNode = currNode.getNext();
  }
  return result;
}

search() method

Write a search() method that traverses the entire list trying to find a node that matches the argument value – if found, return the true, otherwise return false. Add boolean search(T data) signature the the List.java interface.

  • Implementation of search() is similar to toString() (write a while loop)

Submit your List.java, Node.java and LinkedList.java with the search() method to gradescope.

search() solution

public boolean search(T data) {
  Node<T> currNode = head;
  while (currNode != null) {
    if (currNode.getData().equals(data)) {
      return true;
    }
    currNode = currNode.getNext();
  }
  return false;
}

Linear search – complexity

What we have implemented is an unsorted linked list – how many comparisons does it required to find a target element?

In the worst case, each node must be examined once, resulting in O(n) complexity – n is the number of elements in the list.

Delete node

Removing a node takes a few more steps:

  • Find the node to remove (if present, can find it through index or matching data), look ahead for the next node, adjust reference for the previous node
  • How to remove head?

implement pop(int index)

Implement pop(int index), get(int index), and set(int index, T value) in your LinkedList.java and submit it alongside with List.java and Node.java to gradescope.

Find index and remove

public T pop(int index) {
  int count = 0;
  Node<T> previous = null;
  Node<T> cur = head;
  while (cur != null) {
    if (count == index) {
      if (previous != null) previous.setNext(cur.getNext());
      return cur.getData();
    }
    previous = cur;
    cur = cur.getNext();
    count++;
  }
  return null;
}

Complete LinkedList solution

public class LinkedList<T> implements List<T>{
    private Node<T> head;
    private Node<T> tail;
    private int length;
    
    public LinkedList() {
        head = null;
        tail = null;
        length = 0;
    }

    public void append(T value) {
        Node<T> nodeToAdd = new Node<>(value);
        if (head == null) {
            head = nodeToAdd;
            tail = nodeToAdd;
        } else {
            tail.setNext(nodeToAdd);
            tail = nodeToAdd;
        }

    };

    public void prepend(T value) {
        Node<T> nodeToAdd = new Node<>(value);
        if (head == null) {
            head = nodeToAdd;
            tail = nodeToAdd;
        } else {
            nodeToAdd.setNext(head);
            head = nodeToAdd;
        }
        length++;
    }

    public void insert(int index, T value) {
        Node<T> nodeToAdd = new Node<>(value);
        int count = 0;
        Node<T> previous = null;
        Node<T> cur = head;
        while (cur != null) {
            if (count == index) {
                nodeToAdd.setNext(cur);
                if (previous != null) previous.setNext(nodeToAdd);
                else head = nodeToAdd;
                return;
            }
            previous = cur;
            cur = cur.getNext();
            count++;
        }
    }

    public int length() {return length; }

    public T get(int index) {
        int count = 0;
        Node<T> cur = head;
        while (cur != null) {
            if (count == index) {
                return cur.getData();
            }
            cur = cur.getNext();
            count++;
        }
        return null;
    }

    public void set(int index, T value) {
        int count = 0;
        Node<T> cur = head;
        while (cur != null) {
            if (count == index) {
                cur.setData(value);
                return;
            }
            cur = cur.getNext();
            count++;
        }
    }


    public T pop(int index) {
        int count = 0;
        Node<T> previous = null;
        Node<T> cur = head;
        while (cur != null) {
            if (count == index) {
                if (previous != null) previous.setNext(cur.getNext());
                return cur.getData();
            }
            previous = cur;
            cur = cur.getNext();
            count++;
        }
        return null;
    }

    public String toString() {
        String out = "";
        Node<T> cur = head;
        while (cur != null) {
            out += cur.getData() + " ";
            cur = cur.getNext();
        }
        return out;
    }

    public boolean search(T data) {
        Node<T> currNode = head;
        while (currNode != null) {
            if (currNode.getData().equals(data)) {
                return true;
            }
            currNode = currNode.getNext();
        }
        return false;
    }

}

Runtime of LinkedList

Front of List Middle of List End of List
get O(1) O(n) O(1)
set O(1) O(n) O(1)
insert O(1) O(n) O(1)
remove O(1) O(n) O(1)

Other types of linked list

We just implemented a singly linked list

Doubly linked list:

Other types of linked list

Circular linked list: