linear search
loops and searches each element until the target is located
time: O(n), as time to search increases linearly in relation to the number of items in the list
space: O(1), constant amount of space
linear search python
def linearsearch(arr, target):
for i in range(len(arr)):
if arr[i] == target:
print("Target found")
return i
print("Target not found")
return -1binary search
time is o(log n) as the array is divided in half at each step
space is o(1) as it uses a constant amount of extra space
conditions necessary for a binary search
the list needs to be ordered
binary search python
low = 0
high = len(arr) - 1
while low <= high:
mid = (low + high)//2
if arr[mid] == target:
print(f"target at index {mid}")
break
elif target > arr[mid]:
low = mid + 1
elif target < arr[mid]:
high = mid - 1insertion sort
swaps/orders in one iteration by inserting data in correct place
uses less memory and time than bubble sort
time is o(n^2)
space is o(1) as it uses a constant amount of extra space
insertion sort python
for i in range(1, len(list)):
cIndex = i
while (cIndex > 0) and (list[cIndex - 1] > list[cIndex]):
list[cIndex], list[cIndex - 1] = list[cIndex - 1], list[cIndex]
cIndex -= 1bubble sort
Bubble sort compares adjacent elements in the list/array.
They are swapped if the elements are in the wrong order
The algorithm iterates through the array multiple times in passes, using a swap flag
On each pass, the largest unsorted element gets sorted to its correct position at the end of the array.
The process is repeated until the entire array is sorted.
bubble sort python
for i in range(len(array) - 1): #-1 for zero indexing
swapped = False
for j in range(len(array)-i-1): #compares all adjacent pairs. i stays the same during this inner loop, j increments
if array[j] > array[j +1]:
array[j], array[j+1] = array[j+1], array[j]
swapped = True
if swapped is False:
break ADTs and examples
Abstract Data Type
linked list
queue
stack
binary tree
linked list
a dynamic data structure of nodes and pointers, where items are not stored contiguously in memory.
the first node is the head, the last node is the tail
binary tree
an ADT with nodes arranged hierarchally starting with root at top, where each node has 2 descendants.
left pointer -> left subtree, right pointer -> right subtree
three modes of traversal:
- in order
- preorder
- post order
post order traversal
python:
def postorder(self):
if self.left is not None:
self.left.postorder()
if self.right is not None:
self.right.postorder()
print(self.data)pre order traversal
python:
def preorder(self):
print(self.data) #prints first node it sees
if self.left is not None:
self.left.preorder()
if self.right is not None:
self.right.preorder()in order traversal
using 2D arrays
def InOrder(ArrayNodes, RootNode):
if ArrayNodes[RootNode][0] != -1: #if left node is not empty
InOrder(ArrayNodes, ArrayNodes[RootNode][0]) #recursively call to search left node
print(str(ArrayNodes[RootNode][1])) #print current node
if ArrayNodes[RootNode][2] != -1:
InOrder(ArrayNodes, ArrayNodes[RootNode][2])def inorder(self): #in order traversal, with printing data
if self.left is not None:
self.left.inorder() #keeps calling as long as there is a left node to search,
print(self.data) #then prints. starts from smallest to largest
if self.right is not None: #unwinds, searches right subtree of "previous" node
self.right.inorder()insertion binary tree
def insert(self, data):
if self.data is None:
self.data = data
else:
if data < self.data: #if the data is less than the current node's data
if self.left is not None: #if the left subtree exists/a left node exists
self.left.insert(data) #recursively call, checking left subtree
else:
self.left = Node(data) #insert into current node's left if no left subtree exists
elif data > self.data:
if self.right is not None:
self.right.insert(data)
else:
self.right = Node(data)
else: #if data is equal to current node's data, do nothing (duplicate)
return
stack
a LIFO linear data structure. items are inserted and removed from the top/head
push, pop, peek, isEmpty, isFull, size etc
justify using a linked list over an array to implement a stack (review this)
linked list is not restricted in size (dynamic data structure)
has greater freedom to expand to contract by adding or removing the nodes as necessary
allows more efficient editing using pointers
queue
a FIFO data structure. deletions occur at the head, and insertions at the back
enqueue, dequeue, peek, size, isEmpty, isFull
dictionary
made up of associated key-value pairs. values are accessed using a key
graph
a graph is a collection of nodes or vertices between which there are edges.
big O notation
mathematical notation used to describe the performance or complexity of an algorithm in relation to the time taken or the memory used for the task
bubble sort big o (time and space)
TIME
best case: o(n)
on average: o(n^2)
worst case: o(n^2)
SPACE: o(1)
insertion sort big o (time and space)
TIME
best case: o(n)
on average: o(n^2)
worst case: o(n^2)
SPACE: o(1)