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

Quiz 08

You have 10 minutes to complete the quiz.

Choose one option for each of the 4 questions.

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?

Methods

  • Insertion
  • Search
  • Deletion

A toString() method to test our code

insert(Node) method

Write a insert(Node)

  • What are the options of implementation for insertion?
  • What are the advantages and disadvantages of each?

Insert at end of list

public void insert(Node<T> node) {
  if (head == null) head = node;
  else {
    Node<T> currNode = head;
    while (currNode.getNext() != null) {
      currNode = currNode.getNext();
    }
    currNode.setNext(node);
  }
}

toString() method

Before we move on to other types of insert, lets 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;
}

Other insertion methods?

Our insert method has to traverse the entire list to insert a new node.

How can we improve on insertion time?

Insert at the beggining of list

public void push(Node<T> node) {
  if (head == null) head = node;
  else {
    node.setNext(head);
    head = node;
  }
}

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 node, if not, return null

  • Similar to toString()

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?

Search matching data and remove

public Node<T> remove(T data) {
  Node<T> currNode = head;
  Node<T> previous = null;
  while (currNode != null) {
    if (currNode.getData().equals(data)) {
      if (previous != null) {
        previous.setNext(currNode.getNext());
      } else {
        head = head.getNext();
      }
      return currNode;
    } else {
      previous = currNode;
      currNode = currNode.getNext();
    }
  }
  return null;
}

Remove first element of the list

public Node<T> pop() {
  Node<T> node = null;
  if (head != null) {
    node = head;
    head = head.getNext();
  }
  return node;
}

Remove Nth node

public Node<T> pop(int n) {
  if (n >= 0 && n < length) {
    Node<T> current = head;
    Node<T> previous = null;
    for (int i = 0; i < n; i++) {
      previous = current;
      current = current.getNext();
    }
    if (previous == null) return pop();
    else previous.setNext(current.getNext());
    length--;
    return current;
  }
  return null;
  
}

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:

Recursion

Many linked list functions can be nicely implemented with recursion.

public Node<T> search(Node<T> node, T value) {
  if (node == null) { // base case
    return null;
  }
  if (node.getData().equals(value)) {
    return node; // found node with matching value
  }
  // search next
  return search(node.getNext(), value);
  
}

public Node<T> search(T value) {
  // traverse all nodes, return the first
  // node that data equals value argument
  return search(head, value);
}

Comparable

Node.java

public class Node<T extends Comparable<T>> implements Comparable<Node<T>> {
    private T data;
    private Node<T> next;

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

    public T getData() {
        return data;
    }

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

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

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


    public int compareTo(Node<T> o) {
        if (this == o) {
            return 0;
        }

        if (o == null) {
            return -1;
        }

        Node<T> aNode = (Node<T>) o;
        return this.data.compareTo(aNode.getData());
    }
}

Comparable

LinkedList.java

public class LinkedList<T extends Comparable<T>> {
    public Node<T> head;
    public Node<T> tail;
    private int length;

    public LinkedList(Node<T> node) {
        head = node;
        tail = node;
        length++;
    }

    public void addNode(Node<T> node) {
        tail.setNext(node);
        tail = node;
        length++;
    }

    public int length() {
        return length;
    }

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

    public Node<T> search(T value) {
        // traverse all nodes, return the first
        // node that data equals value argument
        Node<T> current = head;
        while (current != null) {
            if (current.getData().equals(value)) {
                return current;
            }
            current = current.getNext();
        }
        // no node with value T, return null
        return null;
    }

    public void remove(Node<T> node) {
        // remove head?
        if (head == node) {
            head = head.getNext();
        }
        // remove matching object
        Node<T> current = head;
        Node<T> previous = null;
        while (current != null) {
            // check if objects are the same
            if (current == node) {
                previous.setNext(current.getNext());
                length--;
            }
            previous = current;
            current = current.getNext();
        }

    }

    public void remove(T value) {
        // remove first node with data equals value
        // remove head?
        if (head.getData().equals(value)) {
            head = head.getNext();
        }
        // remove matching object
        Node<T> current = head;
        Node<T> previous = null;
        while (current != null) {
            // check if objects are the same
            if (current.getData().equals(value)) {
                previous.setNext(current.getNext());
                length--;
            }
            previous = current;
            current = current.getNext();
        }
    }

    public void remove(int position) {
        // remove node at Nth position
        // remove first node with data equals value
        // remove head?
        if (position == 0) {
            head = head.getNext();
        }
        // remove matching object
        Node<T> current = head;
        Node<T> previous = null;
        int counter = 1;
        while (current != null) {
            // check if objects are the same
            if (counter == position) {
                previous.setNext(current.getNext());
                length--;
            }
            previous = current;
            current = current.getNext();
            counter++;
        }
    }

    public void sort() {
        // lazy bubble sort
        for (int i=0; i < length; i++) {
            Node<T> current = head;
            while (current != null) {
                //System.out.println(current.getData() + " " + current.getNext().getData() + " " + current.compareTo(current.getNext()));
                if (current.compareTo(current.getNext()) > 0) {
                    T temp = current.getData();
                    current.setData(current.getNext().getData());
                    current.getNext().setData(temp);
                }
                current = current.getNext();
            }
        }
    }
}

Use and test LinkedList

public class UseList {
    public static void main(String[] args) {
        Node<String> node = new Node<>("tomato");
        LinkedList<String> list = new LinkedList<>(new Node<>("apple"));
        list.addNode(new Node<>("kiwi"));
        list.addNode(node);
        list.addNode(new Node<>("banana"));
        list.addNode(new Node<>("pineapple"));
        System.out.println(list.length());
        System.out.println(list);
        System.out.println(list.search("pear"));
        list.remove(node);

        list.addNode(new Node<>("pear"));
        list.addNode(new Node<>("orange"));
        list.addNode(new Node<>("plum"));
        list.addNode(new Node<>("tomato"));
        list.addNode(new Node<>("lychee"));

        System.out.println(list);
        list.sort();
        System.out.println(list);
    }
}