Lecture5 Lists
Lecture5 Lists
Lecture Note #5
2
1 Use of a List
Motivation
Motivation
❑ List is one of the most basic types of data collection
▪ For example, list of groceries, list of modules, list of friends,
etc.
▪ In general, we keep items of the same type (class) in one list
❑ Typical Operations on a data collection
▪ Add data
▪ Remove data
▪ Query data
▪ The details of the operations vary from application to
application. The overall theme is the management of data
4
ADT of a List (1/3)
❑ A list ADT is a dynamic linear data collection
▪ A collection of data items, accessible one after another
starting from the beginning (head) of the list
5
ADT of a List (2/3)
import java.util.*; ListInterface.java
6
ADT of a List (2/3)
❑ The ListInterface defines the operations (methods) we
would like to have in a List ADT
❑ The operations shown here are just a small sample. An
actual List ADT usually contains more operations.
❑ Here we assume that the List ADT only contains
integer elements
❑ Using indices to access the elements in the list, 1st
element is at index 0 and last element is at index N-1
(where N is the number of elements in the list)
7
ADT of a List (3/3)
❑ We will examine 2 implementations of list ADT, both
using the ListInterface shown in the previous slide
To be discussed
in section 2.
Contractual
obligations: Java Arrays
List ADT
1.Create empty
list
2.Determine … Linked Lists
3.Add an item
To be discussed
…
in section 3:
Basic Linked List
ADT Implementations
8
2 List Implementation via
Array
List Implementation: Array
❑ This is a straight-forward approach
❑ Use Java array of a sequence of n items
num_items arr : array[0..m] of locations
0 1 2 n-1 m
10
Basic operations: Insert an item
◼ To insert an item at position/index i
1. Shift last item to item at index i to the right by 1 to
create a “gap”
2. Insert the new item in the “gap” created
8 a0 a1 a2 a3 a4 a5 a6 a7
11
Basic operations: Insert an item
❑ What happens if the array is already filled
when inserting an item?
1. Enlarge it by creating a new array (usually
doubling the size of original array) and copy
original array over
2. Insert new item as per normal
12
Insert method with array resizing
import java.util.*; ListUsingArray.java
13
Insert method with array resizing
public void enlargeArr() { ListUsingArray.java
int newSize = capacity * 2; // double the size
int[] temp = new int[newSize];
14
Basic operations: Remove an item
◼ To remove/delete an item at position/index i
1. Shift current items from index i+1 and onwards to the
left by 1 to delete the item and close the gap
8 7 a1 a2 a3 a4 a5 a6 a7
unused
15
Remove method
import java.util.*; ListUsingArray.java
// shift item from index+1 onwards to the left to close the gap
for (int i=index+1; i < num_items; i++)
arr[i-1] = arr[i];
num_items--;
return item;
}
}
16
List operations using array implementation
◼ Useful operations
❑ empty() – return true if num_items is 0, false otherwise
❑ size() – return num_items
17
List operations using array implementation
◼ Operations to retrieve/access item at index i in the list
❑ getItemAtIndex(int i) – if i within the bounds of the array
return arr[i]
❑ getFirst() – return getItemAtIndex(0)
❑ getLast() – return getItemAtIndex(num_items-1)
19
Testing Array Implementation of List
import java.util.*; TestListUsingArray.java
System.out.println("Testing removal");
list.removeFront();
list.removeBack();
list.removeAtIndex(1);
list.print();
if (list.contains(1))
list.addFront(6);
list.print();
}
}
20
Analysis of Array Impln of List
◼ Time complexity of the different list operations
❑ Retrieval: getItemAtIndex(int i), getFirst(), getLast()
◼ O(1) – indexing into an array is constant time due to random access
memory of the computer
❑ Insertion: addItemAtIndex(int i, int item), addFront(), addBack()
◼ Best case = O(1) – if adding at the back and no need to enlarge array
◼ Worst case = O(n) – if adding to the front due to shifting all item to the right
or adding to the back but need to enlarge the array so have
to perform copying of n items to new array (is this realistic?)
◼ Amortized analysis (adding at the back) = ? (find out during lecture)
◼ Average case = O(n) – on average need to shift ½(n) items to the right
❑ Deletion: removeItemAtIndex(int i), removeFront(), removeBack()
◼ Best case = O(1) – if removing from the back
◼ Worst case = O(n) – if removing from the front due to shifting all items to
the left
◼ Average case = O(n) – on average need to shift ½(n) items to the left
21
Analysis of Array Impln of List
◼ What about the Space Complexity?
❑ In the best case, we use exactly n space for n items
❑ In the worst case, we use 2n space for n+1 item where n is
multiple of 2
❑ Either way, the space complexity is O(n)
22
3 List Implementation via
Linked List
3.1 Linked List Approach
❑ Idea
❑ Each item in the list is stored in a node, which also contains a next
pointer that references/points to the node to its right (its neighbour)
❑ Order the nodes by associating each with its neighbour(s)
❑ Allow elements in the list to occupy non-contiguous memory
item next item next
ai ai+1
…
24
3.3 ListNode – contain integer element
ListNode.java
class ListNode {
/* attributes */
public int item;
public ListNode next;
/* constructors */
public ListNode(int val) { this(val, null); }
public ListNode(int val, ListNode n) {
item = val;
next = n; Mark this slide – You may need to refer to it later
} when we study the different variants of linked list.
/* get the next ListNode */
public ListNode getNext() { return next; }
head
represents null
a0 a1 a2 a3
26
3.5 Basic Linked List
◼ Using ListNode to define BasicLinkedList
import java.util.*; BasicLinkedList.java
…
}
27
Basic Operations: Accessing items in the LL
◼ To access an item at index i in the LL we need to have a reference
pointing to its node (cannot directly access the node like an array)
Want to access a1
head at index 1
a0 a1 a2 a3
a0 a1 a2 a3
29
Basic Operations: Insert an item
◼ Insert an item at index i in the linked list
1. Create a new node n containing the item
2. Access the node cur at index i-1
3. Point next reference of n to neighbor of cur
4. Point next reference of cur to n
5. Increment number of nodes
30
Basic Operations: Insert an item
1. Create node
n
2. Access node at index 1
Want to insert
num_nodes head cur
a4 at index 2
4
a0 a1 a2 a3
a4 a0 a1 a2 a3
n head
2. Point n.next to a4 a0 a1 a2 a3
head
n head
3. Set head to n a4 a0 a1 a2 a3
32
Insert an item – Special cases?
2. When the list is empty
▪ New item is added to the front of the list therefore this is same
case as 1.
33
Insert an item: insert(ListNode cur, ListNode n)
◼ Code for inserting a new node n at index i
given reference cur to node at index i-1
import java.util.*; BasicLinkedList.java
35
Basic Operation: Remove an item
◼ Remove an item at index i in the linked list
1. Access the node cur at index i-1
2. Point next reference of cur to neighbor of neighbor of cur
3. Decrement number of nodes
1. Access node at index 1
Want to remove
num_nodes head cur node at index 2
4
a0 a1 a2 a3
3
a0 a1 a2 a3
36
Remove an item – Special case
▪ When removing from the front of the linked list (i.e index
0) → In this case cur == null (no node before index 0)
1. Point head to head.next
2. decrement num_nodes
Want to remove
head node at index 0
num_nodes
4 a0 a1 a2 a3
2. Decrement num_nodes
num_nodes head 1. Set head to head.next
3
a0 a1 a2 a3
37
Remove an item: remove(ListNode cur)
◼ Code for removing a node at index i given
reference cur to node at index i-1
import java.util.*; BasicLinkedList.java
39
3.5 Test Basic Linked List
◼ Example
import use
java.util.*; TestBasicLinkedList.java
System.out.println("Testing removal");
list.removeFront();
list.removeBack();
list.removeAtIndex(1);
list.print();
if (list.contains(1))
list.addFront(6);
list.print();
}
}
[CS1020 Lecture 10: List ADT & Linked Lists]
40
Analysis of Linked List Implementation of List
◼ Time complexity of the different list operations
❑ Retrieval: getItemAtIndex(int i), getFirst(), getLast()
◼ Best case = O(1) – accessing the first node, return the head
◼ Worst case = O(n) – accessing the last node, since you need to move all
the way to the back from the head (n moves)
◼ Average case = O(n) – need to move about half way through the list to
access any node on average so ½(n) iterations of
the for loop
❑ Insertion: addItemAtIndex(int i, int item), addFront(), addBack()
◼ Best case = O(1) – if adding at the front (don’t have to worry about
enlarging the list unlike array)
◼ Worst case = O(n) – if adding to the back due to having to move all the
way to the back from the head (n moves)
◼ Average case = O(n) – on average need to make ½(n) moves
41
Analysis of Linked List Implementation of List
❑ Deletion: removeItemAtIndex(int i), removeFront(), removeBack()
◼ Best case = O(1) – if removing from the front
◼ Worst case = O(n) – if removing from the back, again due to moving all the
way to the back from the head
◼ Average case = O(n) – on average need to make ½(n) moves
42
When to use Array vs LL implementation of List
43
4 More Linked Lists
tail
head
num_nodes
4 a0 a1 a2 a3
45
4.1 Tailed Linked List
◼ We create a new class TailedLinkedList:
TailedLinkedList.java
import java.util.*;
…
public ListNode getTail(); // method to return tail
}
46
Accessing an item: getItemAtIndex(int i)
class TailedLinkedList implements ListInterface { TailedLinkedList.java
47
Inserting in a tailed linked list
◼ Inserting at index == size(), Immediately insert using the tail
without having to start from head and moving to the back.
1. Create the node n to be inserted & set cur to tail
2. Point next reference of cur to n 1. Create node & set cur to tail
n
3. Set tail to n
head cur tail a4
a0 a1 a2 a3
n
2. Point tail.next to n
a0 a1 a2 a3
n
3. Set tail to n
a0 a1 a2 a3
48
No Free lunch – Extra book keeping in insert()
◼ In the insert method need to update tail
1. When inserting to tail (illustrated in previous slide)
2. When inserting into empty list (tail == null)
TailedLinkedList.java
public void insert(ListNode cur, ListNode n) {
if (cur == null) { // insert in front of list
n.setNext(head);
head = n; // update head
if (tail == null) // update tail if list originally empty
tail = head; Case 2
}
else { // insert anywhere else
n.setNext(cur.getNext());
cur.setNext(n);
if (cur == tail) // update tail if insert new last item
tail = tail.getNext(); Case 1
}
num_nodes++;
}
49
Removing from a tailed linked list
◼ Need to update tail reference whenever last item
and only item is removed (NO FREE LUNCH!)
class TailedLinkedList implements ListInterface {
System.out.println("Testing removal");
list.removeFront();
list.removeBack();
list.removeAtIndex(1);
list.print();
if (list.contains(1))
list.addFront(6);
list.print();
}
}
[CS1020 Lecture 10: List ADT & Linked Lists]
51
5 Other Variants
tail
head
num_nodes
4 a0 a1 a2 a3
53
Doubly Linked List
◼ In the preceding discussion, we have a “next” pointer to
move forward
◼ Often, we need to move backward as well
◼ Use a “prev” pointer to allow backward traversal
◼ Once again, no free lunch – need to maintain “prev” in all
updating methods
◼ Instead of ListNode class, need to create a DListNode
class that includes the additional “prev” pointer
prev next
x2
node
54
Doubly Linked List
◼ How a doubly linked list looks (has both head and tail
reference)
head tail
num_nodes
4 x1 x2 x3 x4
55
Usefulness of Doubly Linked List
◼ Reduce moving of references
❑ When adding/removing/accessing a particular index,
you can start from the end (front or back) that is closer
to the index
❑ Does not change time complexity of the List
operations but improves the constant which is still
important in practice! (especially in beating time limit
constraints of lab assignments …)
56
6 Java API: ArrayList class and
LinkedList class
59
Eg Using ArrayList
◼ Declaring an ArrayList variable/reference
❑ To declare an ArrayList reference, specify the type of the object it
contains. For example,
ArrayList<Integer> list;
list is a reference to an Arraylist containing Integer objects
◼ Creating an ArrayList object
❑ Same as declaring an ArrayList reference need to specify the type of
object it contains. For example,
list = new ArrayList<Integer>();
list now points to an ArrayList object that contains Integers
◼ Make sure the reference type and object type is the
same/compatible
◼ LinkedList is used in the same manner
60
Why “reinvent the wheel”?
◼ In a data structures course, students are often
asked to implement well-known data structures.
◼ A question we sometimes hear from students:
“Since there is the API, why do we need to learn to
write our own code to implement a data structure
like linked list?”
◼ Writing the code allows you to gain an in-depth
understanding of the data structures and their
operations
◼ The understanding will allow you to appreciate their
time complexity analysis and use the API effectively
61
7 Summary
◼ We learn to create our own data structure to
implement the List ADT
❑ Need to be careful with boundary cases
❑ Manipulation of references (The sequence of statements
is important! With the wrong sequence, the result will be
wrong.)
❑ Re-use of codes (Think in terms of basic operations and
how more complex operations can re-use the basic ones
→ removeLast & removeFirst, addLast & addFirst)
❑ Drawings are very helpful in understanding the cases,
which then can help in knowing what can be
used/manipulated
62
End of file