Lists

CSC 385 - Data Structures and Algorithms

Brian-Thomas Rogers

University of Illinois Springfield

College of Health, Science, and Technology

Objectives

  • Understand basics of Lists ADT
  • Understand ArrayLists
  • Understand LinkedLists
  • Discuss the advantages and disadvantages

List ADT

List ADT

  • Have order: each item is associated with a position
  • Lists are a traversable data structure
  • items can be added, removed, retrieved, and modified anywhere in the list
  • Behaves as if it has dynamic size

List Implementations

  • We cover two implementations for lists
    • Array Lists: represented internally as a contiguous array of objects
      • Supports random access
    • Linked Lists: represented internally as a chain of connected nodes via reference objects
      • Does not support random access

List Interface

public interface List<T> {
    /*Returns item at index*/
    public T get(int index);

    /*Adds obj to position index*/
    public boolean add(int index, T obj);

    /*Sets item at index to obj*/
    public T set(int index, T obj);

    /*Removes item at index*/
    public boolean remove(int index);

    /*Removes first occurrence of obj*/
    public boolean remove(T obj);

    /*Returns the index of the first occurrence
    of obj in the list*/
    public int indexOf(T obj);
}

List Adding

  • There are many methods we can implement for adding
  • The list interface only shows 1 but we can also have
    • prepend: Adds elements to the front of the list like a Queue
    • append: Adds elements to the back of the list like a stack
    • insert: Adds elements in the middle of the list
  • The first two can be provided along with the add but sometimes insert is the name used in other languages
  • This is not an exhaustive list of possible add methods

List Removing

  • Like adding, we can implement some additional removal methods which are
    • removeFront: Removes and returns the element at the front of the list similar to dequeue
    • removeLast: Removes and returns the last element of the list similar to pop
  • This is not an exhaustive list of possible remove methods

Java List Interface

Java 17 Oracle List Interface Documentation

ArrayList

ArrayList

  • Implementation of the List interface based on arrays
  • Restriction using arrays include
    • capacity of array must be specified when the array is initialized
    • we don’t necessarily want to put a limit nor do we want to allocate too much
    • So we need to start small and then expand as needed
    • Data must be kept next to eachother with no “empty” spaces between each object

Implementing ArrayList

  • Data fields used in the book
    • T[] array: Array of T objects
    • size: An integer that counts the number of entries
    • INITIAL_CAPACITY: Used to store initial capacity of the array

Quick Note

  • The book uses 1-based indexing meaning it starts the index at 1
  • Our implementation will use 0-based indexing which is the most common and easier to write for
  • The following will be based on the implementation in the class

ArrayList Constructor

  • When we initialize an ArrayList with the default constructor we must
    • Create an empty default-sized array
    • Set size of the list to zero
  • This can be done within the clear method
  • We can also allow a constructor to accept an initial capacity to set the array

ArrayList Invalid Index

  • You might consider an invalid index to be similar to arrays where it is just a value less than 0 or a value greater than or equal to array.length
  • Using the array length as invalid index can create invalid lists
  • Consider the following list that is partially filled
index 0 1 2 3 4 5 6
10 20 30 40 null null null
  • The array has a size 7 but the list has size 4. We cannot insert an item at indices 5 or 6 else there will be an empty space or 2 between the inserted item and the rest of the list.
  • Thus it is important to use size instead of array.length when validating indices

ArrayList Invalid Index Cont.

  • Additional caveat is depending on what operation we are performing, the value stored in size might be valid or invalid.
  • Suppose we have the following array
index 0 1 2 3 4 5 6
10 20 30 40 null null null
  • We can add at index 4 (which is equal to size in this case) even though it is not “in” the list but is just an append
  • We cannot, however, remove at index 4 since nothing exists there.
  • This is true for both Array and Linked Lists.

ArrayList Retrieval

  • get(int index)
    • Retrieves item at index if index is valid
    • Has a running time \(O(1)\) due to random access of arrays
    • Should perform error checking when index is invalid

Arraylist Searching

  • indexOf(T obj)
    • Returns index of the first occurrence of obj.
    • Returns -1 if item does not exist.
    • Because the list must be traversed the running time is \(O(n)\)

ArrayList Searching Cont.

  • contains(T obj)
    • Calls indexOf method to do a sequential search
    • Returns true if the specified item is in the list (indexOf does returns anything other than -1)
    • Because we call indexOf this is also \(O(n)\)

ArrayList Modification

  • set(int index, T obj)
    • Replaces existing item at the specified index
    • Must perform error checking for invalid index
    • Due to random access nature of arrays this runtime is \(O(1)\)

ArrayList Add

  • add(int index, T obj)
    • Inserts an item at a specified index
    • Should validate index
    • If size == array.length then resize() is called
    • Items to the right of the insertion point must be shifted on position to the left to make room for the new item
    • Due to the shifting and possible resizing, has \(O(n)\) runtime

ArrayList Add Example

Making Room to add Carla at index 2

ArrayList Append

  • append(T obj)
    • Adds an item to the end of the list
    • If size == array.length then resize() is called
    • Item is inserted to the end
    • If no resize is performed then the runtime is \(O(1)\) else \(O(n)\)

ArrayList Prepend

  • prepend(T obj)
    • Adds an item to the beginning of the list
    • If size == array.length then resize() is called
    • Item is inserted at index 0 which means elements must be shifted 1 position to the right
    • Due to the shifting of elements, the runtime is \(O(n)\)

ArrayList Resize

  • Since arrays cannot dynamically change size we must perform the following
    • Create a new array that is larger than the current array length
    • Copy all elements from the original array to the new array
    • Set the instance variable array to the new array

ArrayList Remove

  • remove(int index)
    • Removes element at index
    • Validates index
    • All elements to the right of removed item must be shifted left to ensure no empty space
    • Running time is \(O(n)\) due to shifting

ArrayList Remove Cont.

  • remove(T obj)
    • Calls indexOf method to get first occurrence of obj if it exists
    • Calls remove(index)
    • Returns false if the item does not exist
    • Running time is \(O(n)\)

ArrayList Remove Example

Removing Bob by shifting array entries

Java ArrayList

Java 17 Oracle Documentation

Linked List

LinkedList Implementations

  • There are several ways to implement Linked Lists
    • Singly Linked List: A list where nodes have 1 reference for the next node in the list
    • Doubly Linked List: A list where nodes have 2 references for the next and previous node in the list
    • Each of these implementations can be implemented with/without dummy nodes
    • And Linked lists with/without tail nodes (tail nodes point to the end of the list whereas head nodes point to the front of the list)
    • Singly linked lists must have at least a head node while doubly linked lists can have one or the other or both
    • Having both head and tail nodes adds some benefits with very little overhead, therefore, the implementations will include both

List Node

  • Since linked list is a chain of nodes we must have the following
    • A data item
    • 1 or 2 references depending on the type of list
  • The Node class is defined as an inner class

Linked List Implementation

Linked List Implementation

  • The following will only be discussing the doubly linked list
  • A sinlgy linked list can only be traversed on one direction while a doubly linked list can be traversed in 2
  • Singly linked list is useful when elements are only accessed at either end of the list (think Deque)
  • Doubly linked list is preferred for any other need of a linked list

LinkedList Data Fields

  • size: Size of the list
  • head: Node that contains the element at the front of the list
  • tail: Node that contains the element at the back of the list

LinkedList Constructor

  • Calls clear
  • If no other setup is necessary for the default constructor then there is no need to explicitly create the constructor and call clear

LinkedList Clear

  • Sets the size 0
  • Sets the head and tail nodes to null

LinkedList Utility Methods

  • There are a couple utility methods which will make life easy for other methods
  • These methods are private
    • findObjectNode(T obj): Traverses the linked list until it finds the node that contains the given item and returns the node else returns null. This must perform a linear search so is \(O(n)\).
    • getNodeAt(int index): Retrieves the node at the given index

LinkedList getNodeAt

  • This method takes an index position of the node we want from the list
  • We should validate the index
  • If the index is valid we can do the following since we are using a doubly linked list and have a head and tail node
    • If the index lies in the first half, traverse from the head to the index
    • If the index lies in the second half, traverse from the tail to the index
  • At worst, half the list is traversed for \(O(n/2)\) precise runtime

LinkedList Retrieval

  • get()
    • Returns the last item in the linked list
    • This is done quickly with the Tail node so is of \(O(1)\)
  • get(int index)
    • Returns item at given index
    • Calls getNodeAt(index) method to retrieve the node
    • Running time is \(O(n/2)\)

LinkedList Searching

  • indexOf(T obj)
    • Works similar to findObjectNode except returns the index of the obj or -1 if it does not exist
    • Is \(O(n)\)
  • contains(T obj)
    • Calls private method findObjectNode and returns true if anything other than null is returned else false
    • Is \(O(n)\)

LinkedList Adding

  • add(int index, T obj)
    • Adds an item to the linked list at a specified index
      • Call getNode(index) to retrieve node
      • Create a new node that contains obj
      • Updates the value of the next and previous for neighboring ndoes
      • Running time is \(O(n/2)\)

LinkedList Adding Demo

G A prev null A C C next null A:ne->C:nw next C:sw->A:se prev B prev null B next null

Before adding B

G A prev null A B B A:ne->B:nw next C C next null C:sw->B:se prev B:sw->A:se prev B:ne->C:nw next

After adding B

LinkedList Remove Node

  • The following is another private utility method
  • removeNode(node)
    • Takes the node to be removed
    • Called by the public remove methods
    • The node’s previous neighbor is altered to point to the node’s next neighbor
    • The node’s next neighbor is altered to point to the node’s previous neighbor
    • Special care should be taken when dealing with head and tail nodes
    • Running time is \(O(1)\)

LinkedList Remove Node Demo

G A prev null A B B A:ne->B:nw next C C next null C:sw->B:se prev B:sw->A:se prev B:ne->C:nw next

Node B To Be Removed

G A prev null A C C next null A:ne->C:nw next B prev null B next null C:sw->A:se prev

Node B Removed

LinkedList Removing

  • remove(int index)
    • Removes and returns item at specified index
    • Retrieves node by calling getNode(node) and then pass it to remove(node)
    • Return element.
    • Running time is \(O(n/2)\)

LinkedList Removing Cont.

  • remove(T obj)
    • Simplys removes the node with obj
    • Calls findObjNode(obj) to retrieve the node containing obj
    • Then pass that node to remove(node)
    • Return false if obj does not exist in the list
    • Else return true
    • Running time is \(O(n)\) due to call to findObjNode

LinkedList Modification

  • set(int index, T obj)
    • Calls getNode(index) to retrieve node at index
    • Replace item in the node with obj
    • Running time is \(O(n/2)\)

Java LinkedList Library

Java 17 Oracle Documentation for LinkedList

G D prev null head D O O D:ne->O:nw next O:sw->D:se prev N N O:ne->N:nw next N:sw->O:se prev E tail E next null N:ne->E:nw next E:sw->N:se prev