Week 2 - Lists
Week 2 - Lists
WARNING
interface
implementation
– Many different systems can use the same library, so only code
tricky manipulations once, rather than in every client system.
– Operations needed?
– Operations needed?
– Client code can have variables that are instances of the data
structure class and can call methods on these variables
A
0 1 2 i n N-1
ith
element
allocated memory
The University of Sydney Page 13
Array-based Lists
Array
Memory
ith
element
allocated memory
The University of Sydney Page 15
Array-based Lists: set(i,e)
A
0 1 2 i n N-1
previous new
ith element ith element
allocated memory
The University of Sydney Page 16
Pseudo-code for get
def get(i):
# input: index i
# output: ith element in list
if i < 0 or i ³ n then
return “index out of bound”
else
return A[i]
A
0 1 2 i n
A
0 1 2 i n
A o
0 1 2 i n
The University of Sydney Page 19
Pseudo-code for insertion
A
0 1 2 i n
A
0 1 2 i n
A
0 1 2 i n
def remove(i):
if i < 0 or i ³ n
return “index out of bound”
e ← A[i]
if i < n-1
for j in [i, i+1, … , n-2] do
A[j] ← A[j+1]
n ← n - 1
return e
Limitations:
– can represent lists up to the capacity of the array (n vs N)
Space complexity:
– space used is O(N), whereas we would like it to be O(n)
Time complexity:
– both get and set take O(1) time
– both add and remove take O(n) time in the worst case
Unlike index, this keeps referring to the same entry even after
insertion/deletion happens elsewhere in the collection.
head
A B C D
x: Node
next
element
∅
head
A B C D
The University of Sydney Page 28
Linked Lists
Memory
return?
∅
head
A B C D
return head
∅
head
A B C D
Time complexity?
head
A B C D
head
x
∅
A B C D
head
x
∅
e A B C D
head
x
∅
e A B C D
head
x
∅
e A B C D
head
x
∅
e A B C D
head
A B C D
head
A B C D
head
A B C D
Time complexity?
head
B C D
head
B C D
∅
head
A B C D
x
p
∅
head
A B C D
x
p
∅
head
A B C D
x
p
∅
head
A B C D
∅
head
A B C D
∅
head
A B C D
∅
head
A B C D
prev next
element
The University of Sydney Page 50
Doubly Linked Lists
nodes
header trailer
NULL NULL
A B C
Instantiate new Node x with element set to e.
Update x.previous to point to p.previous and x.next to point to p.
A B C
e
The University of Sydney Page 52
insertBefore(p,e) – step 2
A B C
e
Update p.prev.next ¬ x and p.prev ¬ x
x p
A B e C
The University of Sydney Page 53
insertBefore(p,e)
A B C
A B x C
e
x p
A B e C
The University of Sydney Page 54
Pseudo-code
return new_node
A B C D
A B C p
A B C
The University of Sydney Page 56
Pseudo-code
def remove(pos):
// remove pos from the list
// assuming it is a legal pos
pos.prev.next ← pos.next
pos.next.prev ← pos.prev
return pos.element
A (doubly) linked list can perform all of the accessor and update
operations for a positional list in constant time.
Method Time
Space complexity is O(n)
first() O(1)
last() O(1)
Time complexity is O(1) for
before(p) O(1)
all operations
after(p) O(1)
insert_before(p, e) O(1)
insert_after(p, e) O(1)
remove(p) O(1)
size() O(1)
is_empty() O(1)
Linked List
– good match to positional ADT
– efficient insertion and deletion
– simpler behaviour as collection grows
– modifications can be made as collection iterated over
– space not wasted by list not having maximum capacity
Arrays
– good match to index-based ADT
– caching makes traversal fast in practice
– no extra memory needed to store pointers
– allow random access (retrieve element by index)
Calling next() returns the next object and advances the cursor or
raises StopIteration()
for x in obj:
// process x
Is equivalent to:
it = x.__iter__()
try:
while True:
x = it.next()
// process x
except StopIteration:
pass
Direct applications
– Keep track of a history that allows undoing such as Web
browser history or undo sequence in a text editor
– Chain of method calls in a language supporting recursion
– Context-free grammars
Indirect applications
– Auxiliary data structure for algorithms
– Component of other data structures
Each “(”, “{”, or “[” must be paired with a matching “)”, “}”, or “]”
– correct: ( )(( )){([( )])}
– correct: ((( )(( )){([( )])}))
– incorrect: )(( )){([( )])}
– incorrect: ({[ ])}
– incorrect: (
def size()
A simple way of implementing the return t + 1
Stack ADT uses an array:
– Array has capacity N def pop()
if isEmpty() then
– Add elements from left to right
return null
– A variable t keeps track of the else
index of the top element t ¬ t - 1
return S[t + 1]
…
S
0 1 2 t N-1
…
S
0 1 2 t
The University of Sydney Page 70
Stack implementation based on arrays
Performance
– The space used is O(N)
– Each operation runs in time O(1)
Qualifications
– Trying to push a new element into a full stack causes an
implementation-specific exception or
– Pushing an item on a full stack causes the underlying
array to double in size, which implies each operation
runs in O(1) amortized time (still O(n) worst-case).
Boundary cases:
– Attempting the execution of dequeue or first on an empty queue signals
an error or returns null
Direct applications
– Waiting lists, bureaucracy
– Access to shared resources (e.g., printer)
– Multiprogramming
Indirect applications
– Auxiliary data structure for algorithms
– Component of other data structures
Queue
Dequeue Enqueue
Shared
Service
The University of Sydney Page 75
Queue implementation based on arrays
normal configuration
Q
0 1 2 start end N-1
wrapped-around configuration
Q
0 1 2 end start N-1
The University of Sydney Page 76
How to get in a wrapped-around configuration
– Enqueue N elements
– Dequeue k < N elements
– Enqueue k’ < k elements
start
end
start
end
end start
Q
0 1 2 start end N-1
Q
0 1 2 end start N-1
def dequeue()
Note that operation
if isEmpty() then
dequeue returns error if
the queue is empty return “queue empty”
else
e ¬ Q[start]
One could alternatively start ¬ (start + 1) mod N
return null size ¬ (size - 1)
return e
Q
0 1 2 start end N-1
Q
0 1 2 end start N-1
The University of Sydney Page 79
Queue implementation based on arrays
Performance
– The space used is O(N)
– Each operation runs in time O(1)