Skip to main content

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