The Array-Backed List
Agenda
- The List Abstract Data Type (ADT)
- A List Data Structure
- Our List API
- Getting started: how to store our data?
- Built-in
list
as array - The
ArrayList
data structure
1. The List Abstract Data Type (ADT)
An abstract data type (ADT) defines a conceptual model for how data may be stored and accessed.
A list ADT is a data container where:
- values are ordered in a sequence
- each value has at most one preceding and one succeeding value
- a given value may appear more than once in a list
l = [1,1,3,4,1,3]
Other common ADTs (some of which we’ll explore later) include:
- Stacks
- Queues
- Priority Queues
- Maps
- Graphs
2. A List Data Structure
A list data structure is a concrete implementation of the list ADT in some programming language, which, in addition to adhering to the basic premises of the ADT, will also typically support operations that:
- access values in the list by their position (index)
- append and insert new values into the list
- remove values from the list
The implementation of any data structure will generally rely on simpler, constituent data types (e.g., “primitive” types offered by the language), the choice of which may affect the runtime complexities of said operations.
3. The List API
The operations we’ll be building into our list data structures will be based on the common and mutable sequence operations defined by the Python library.
class List:
### subscript-based access ###
def __getitem__(self, idx):
"""Implements `x = self[idx]`"""
pass
def __setitem__(self, idx, value):
"""Implements `self[idx] = value`"""
pass
def __delitem__(self, idx):
"""Implements `del self[idx]`"""
pass
### stringification ###
def __repr__(self):
"""Supports inspection"""
return '[]'
def __str__(self):
"""Implements `str(self)`"""
return '[]'
### single-element manipulation ###
def append(self, value):
pass
def insert(self, idx, value):
pass
def pop(self, idx=-1):
pass
def remove(self, value):
pass
### predicates (T/F queries) ###
def __eq__(self, other):
"""Implements `self == other`"""
return True
def __contains__(self, value):
"""Implements `value in self`"""
return True
### queries ###
def __len__(self):
"""Implements `len(self)`"""
return len(self.data)
def min(self):
pass
def max(self):
pass
def index(self, value, i, j):
pass
def count(self, value):
pass
### bulk operations ###
def __add__(self, other):
"""Implements `self + other_array_list`"""
return self
def clear(self):
pass
def copy(self):
pass
def extend(self, other):
pass
### iteration ###
def __iter__(self):
"""Supports iteration (via `iter(self)`)"""
pass
4. Getting started: how to store our data?
class List:
def ini():
pass
def append(self, value):
pass
def __getitem__(self, idx):
"""Implements `x = self[idx]`"""
pass
def __setitem__(self, idx, value):
"""Implements `self[idx] = x`"""
pass
def __repr__(self):
"""Supports inspection"""
pass
5. Built-in list
as array
To use the built-in list as though it were a primitive array, we will
constrain ourselves to just the following APIs on a given list lst
:
lst[i]
for getting and setting values at an existing, positive indexi
len(lst)
to obtain the number of slotslst.append(None)
to grow the list by one slot at a time (later maybe not one slot at a time)del lst[len(lst)-1]
to delete the last slot in a list
6. The ArrayList
data structure
class MyArrayList:
def __init__(self):
self.data = []
def append(self, value):
self.data.append(value)
def __getitem__(self, idx):
"""Implements `x = self[idx]`"""
assert(isinstance(idx, int))
return self.data[idx]
def __setitem__(self, idx, value):
"""Implements `self[idx] = x`"""
assert(isinstance(idx, int))
self.data[idx] = value
def __delitem__(self, idx):
"""Implements `del self[idx]`"""
assert(isinstance(idx, int))
for i in range(idx+1, len(self.data)):
self.data[i-1] = self.data[i]
del self.data[len(self.data)-1]
def __len__(self):
"""Implements `len(self)`"""
len(self.data)
def __repr__(self):
"""Supports inspection"""
return "[" + ",".join([str(x) for x in self.data]) + "]"
x = MyArrayList()
x.append(1)
x.append(2)
x.append(3)
x
[1,2,3]
y = MyArrayList()
y.append(1)
y.append(3)
y.append(4)
del y[0]
y
[3,4]
7. The ArrayList
data structure (now for real, not quite)
- Array API:
- create array of size
n
- access element at position
i
- set the element at position
i
- get length of array
len(array)
- create array of size
class MyArray:
def __init__(self,n):
self.data = [None] * n
self.len = n
def __getitem__(self, idx):
"""Implements `x = self[idx]`"""
assert(isinstance(idx, int) and self.len > idx)
return self.data[idx]
def __setitem__(self, idx, value):
"""Implements `self[idx] = x`"""
assert(isinstance(idx, int) and self.len > idx)
self.data[idx] = value
def __len__(self):
"""Implements `len(self)`"""
return self.len
def __repr__(self):
"""Supports inspection"""
return "[" + ",".join([str(x) for x in self.data]) + "]"
x = MyArray(3)
x[0] = 'a'
x[1] = 'b'
x[2] = 'c'
x
[a,b,c]
x[2]
'c'
len(x)
3
class MyActualArrayList:
def __init__(self,n=10):
self.data = MyArray(n)
self.len = 0
def append(self, value): # append, yah in O(n)
if len(self.data) <= self.len:
newa = MyArray(2 * len(self.data)) # n
for i in range(0, self.len): # n
newa[i] = self.data[i] # n
self.data = newa # 1
self.data[self.len] = value # 1
self.len += 1 # 1
def __getitem__(self, idx): # O(1)
"""Implements `x = self[idx]`"""
assert(isinstance(idx, int) and idx < self.len)
return self.data[idx]
def __setitem__(self, idx, value): # O(1)
"""Implements `self[idx] = x`"""
assert(isinstance(idx, int) and idx < self.len)
self.data[idx] = value
def __delitem__(self, idx):
"""Implements `del self[idx]`"""
assert(isinstance(idx, int) and idx < self.len)
#print(f"index: {idx}, array: {self.data}, {len(self.data)}")
for i in range(idx+1, self.len):
self.data[i-1] = self.data[i]
#newa = MyArray(self.len - 1)
#for i in range(0, len(self.data) - 1):
# date[i] = self.data[i]
#self.data = newa
self.len += -1
def __len__(self):
"""Implements `len(self)`"""
return self.len
def __repr__(self):
"""Supports inspection"""
return self.data.data[0:self.len].__repr__()
x = MyActualArrayList(5)
x.append(1)
x.append(2)
x.append(3)
x
[1, 2, 3]
x.data
[1,2,3,None,None]
y = MyActualArrayList()
y.append(1)
y.append(3)
y.append(4)
del y[1]
y
[1, 4]
l = []
n = 100000
for i in range(0,n):
l.append(1)
len(l)
100000
n = 100000
l = MyActualArrayList()
for i in range(0,n):
l.append(1)
len(l)
100000
Discussion (Runtime Complexity)
- insertion: amortized \(O(1)\)
- deletion: \(O(n)\)
- access and element at position i: \(O(1)\)
- length: \(O(1)\)