Arrays and Hashing
Dynamic Array
Dynamic array is an array that grows in size as more elements are added to it. It is implemented by creating a new array with double the size of the original array and copying the elements from the original array to the new array.
class DynamicArray:
def __init__(self):
self.capacity = 1
self.size = 0
self.arr = [0] * self.capacity
def add(self, element):
if self.size == self.capacity:
self.capacity *= 2
new_arr = [0] * self.capacity
for i in range(self.size):
new_arr[i] = self.arr[i]
self.arr = new_arr
self.arr[self.size] = element
self.size += 1
def get(self, index):
if index < 0 or index >= self.size:
return -1
return self.arr[index]
def remove(self, index):
if index < 0 or index >= self.size:
return
for i in range(index, self.size - 1):
self.arr[i] = self.arr[i+1]
self.size -= 1
Two Pointers
Two pointers is a technique where two pointers are used to solve a problem. The two pointers can be used to solve problems where we need to find a pair of elements that satisfy a condition.
def two_pointers(arr, target):
left = 0
right = len(arr) - 1
while left < right:
if arr[left] + arr[right] == target:
return [left, right]
elif arr[left] + arr[right] < target:
left += 1
else:
right -= 1
return [-1, -1]
Hash Usage
Hash is a data structure that stores key-value pairs. It is used to store elements in a way that allows for fast retrieval of elements.
hash_map = {}
hash_map[1] = "one"
hash_map[2] = "two"
hash_map[3] = "three"
print(hash_map[1]) # one
print(hash_map[2]) # two
print(hash_map[3]) # three
Hash Implementation
Hash can be implemented using an array of linked lists. The key is hashed to an index in the array and the value is stored in the linked list at that index.
class Hash:
def __init__(self):
self.capacity = 10
self.arr = [None] * self.capacity
def hash(self, key):
return key % self.capacity
def put(self, key, value):
index = self.hash(key)
if self.arr[index] is None:
self.arr[index] = []
self.arr[index].append((key, value))
def get(self, key):
index = self.hash(key)
if self.arr[index] is None:
return None
for k, v in self.arr[index]:
if k == key:
return v
return None
def remove(self, key):
index = self.hash(key)
if self.arr[index] is None:
return
for i, (k, v) in enumerate(self.arr[index]):
if k == key:
self.arr[index].pop(i)
return
Hash Collision
Hash collision is when two keys hash to the same index in the hash table. It can be resolved by using a linked list to store the key-value pairs at that index.
class Hash:
def __init__(self):
self.capacity = 10
self.arr = [None] * self.capacity
def hash(self, key):
return key % self.capacity
def put(self, key, value):
index = self.hash(key)
if self.arr[index] is None:
self.arr[index] = []
for i, (k, v) in enumerate(self.arr[index]):
if k == key:
self.arr[index][i] = (key, value)
return
self.arr[index].append((key, value))
def get(self, key):
index = self.hash(key)
if self.arr[index] is None:
return None
for k, v in self.arr[index]:
if k == key:
return v
return None
def remove(self, key):
index = self.hash(key)
if self.arr[index] is None:
return
for i, (k, v) in enumerate(self.arr[index]):
if k == key:
self.arr[index].pop(i)
return
Hash Set
Hash set is a set that stores unique elements. It is implemented using a hash table where the key is the element and the value is a dummy value.
class HashSet:
def __init__(self):
self.capacity = 10
self.arr = [None] * self.capacity
def hash(self, key):
return key % self.capacity
def add(self, key):
index = self.hash(key)
if self.arr[index] is None:
self.arr[index] = []
for k in self.arr[index]:
if k == key:
return
self.arr[index].append(key)
def contains(self, key):
index = self.hash(key)
if self.arr[index] is None:
return False
for k in self.arr[index]:
if k == key:
return True
return False
def remove(self, key):
index = self.hash(key)
if self.arr[index] is None:
return
for i, k in enumerate(self.arr[index]):
if k == key:
self.arr[index].pop(i)
return
Prefix Sums
Prefix sums is a the technique of storing the sum of all elements in the array.
arr = [1, 2, 3, 4, 5]
prefix_sum = [0] * len(arr)
prefix_sum[0] = arr[0]
for i in range(1, len(arr)):
prefix_sum[i] = prefix_sum[i-1] + arr[i]
print(prefix_sum[2]) # 6 = 3 + 3 = prefix_sum[1] + arr[2] = arr[0] + arr[1] + arr[2] = 1 + 2 + 3
print(prefix_sum[4]) # 15 = 10 + 5 = prefix_sum[3] + arr[4] = arr[0] + arr[1] + arr[2] + arr[3] + arr[4] = 1 + 2 + 3 + 4 + 5