Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Empty file.
25 changes: 25 additions & 0 deletions data_structures_and_algorithms/challenges/quick_sort/quick_sort.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
def quick_sort(arr, left=0, right=None):
if right == None:
right = len(arr) - 1
if len(arr) == 1:
return arr
if left < right:
position = partition(arr, left, right)
quick_sort(arr, left, position - 1)
quick_sort(arr, position + 1, right)
return arr

def partition(arr, left, right):
pivot = arr[right]
low = left - 1
for i in range(left, right):
if arr[i] <= pivot:
low += 1
swap(arr, i, low)
swap(arr, right, low + 1)
return low + 1

def swap(arr, i, low):
temp = arr[i]
arr[i] = arr[low]
arr[low] = temp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions data_structures_and_algorithms/challenges/quick_sort/read.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Challenge Summary
Make a blog post explaining the quick sort algorithm.

## Challenge Description
Review the pseudocode below, then trace the algorithm by stepping through the process with the provided sample array. Document your explanation by creating a blog article that shows the step-by-step output after each iteration through some sort of visual.

Once you are done with your article, code a working, tested implementation of Quick Sort based on the pseudocode provided.

## Approach & Efficiency
The approach I took to this was to split the algorithm into three different functions. One to handle the partition, one to swap the pointers values, and another to hold the logic of when to swap and make partitions, called quick_sort.
The efficiency of this algortihm is a time complexity of
O(nlog(n)) at best and O(n^2) at worst. The space complexity is O(nlog(n)) aswell.

## Solution

#### Steps:
1. We need a list to sort, and we need some logic
2. We start by making three functions, one to separate the list, another to pick a pivot point, and lastly one to compare the pointers to the pivot.
3. Whats a pivot you ask? Well its a point we randomly choose in the list that acts as a comparison. Well whats the point of that? Ill tell you, we set up two pointers, a left and a right, and compare their values to the pivot.
4. Now heres a breakdown, First we cut the list in half, and pick the left chunk. We then choose a pivot, ideally somewhere in the middle of the list. Then we set our left pointer to index 0 of the list, and our right pointer to the last index of the list.
5. We then start comparing the left pointer to the pivot, if the value at its current index is less the pivot, we increment the left pointers index up one. If the left pointers value at the current index is greater than or equal to the pivot, we pause the left and start incrementing the right pointer backwards through the list. We do this until we find a value that is less than the pointer.
6. Once we have our pointers aiming at two out of place values, we swap their values at their indices. So the left pointers value becomes the rights value and vice versa.
7. Once that is done, we repeat the process until the left and right pointer are aiming at the same index.
8. We then split the list we were working on in half and repeat.
9. Eventually we will end up with a beautifully sorted list thanks to the quick sort algorithm.



## PseudoCode

ALGORITHM QuickSort(arr, left, right)
if left < right
// Partition the array by setting the position of the pivot value
DEFINE position <-- Partition(arr, left, right)
// Sort the left
QuickSort(arr, left, position - 1)
// Sort the right
QuickSort(arr, position + 1, right)

ALGORITHM Partition(arr, left, right)
// set a pivot value as a point of reference
DEFINE pivot <-- arr[right]
// create a variable to track the largest index of numbers lower than the defined pivot
DEFINE low <-- left - 1
for i <- left to right do
if arr[i] <= pivot
low++
Swap(arr, i, low)

// place the value of the pivot location in the middle.
// all numbers smaller than the pivot are on the left, larger on the right.
Swap(arr, right, low + 1)
// return the pivot index point
return low + 1

ALGORITHM Swap(arr, i, low)
DEFINE temp;
temp <-- arr[i]
arr[i] <-- arr[low]
arr[low] <-- temp
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Stacks and Queues

we created a data structure called **Stack** and **Queue** in Python. We used three classes, _Node_ and _Queue_, and _Stack_.

### Challenge
- Write queue and stack classes with their methods

## Approach & Efficiency
Stack :
- Define a method called push
- Define a method called pop
- Define a method called peek

Queue:
- Define a method called enqueue
- Define a method called dequeue
- Define a method called peek big
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
class Node:
def __init__(self, value):
self.value = value
self.next = None

def __repr__(self):
return f'{self.value}'


class Stack:

def __init__(self):
'''initialize stack with top, bottom and length'''
self.top = None
self.bottom = None
self.length = 0

def isEmpty(self):
return self.top == None

def push(self, value):
'''adds new node to top of stack by pointing it to self.top'''
node = Node(value)
node.next = self.top
self.top = node
self.length += 1

def pop(self):
'''Takes item from top of stack and returns it by reassigning the current top
to the next item in the stack. Stores the value in a temp variable to be returned'''
if self.length <= 0:
print('nothing to pop')
return

temp = self.top
self.top = self.top.next
popped = temp.value
self.length -= 1
return popped

def peek(self):
'''prints and returns the top of the stack'''
if self.length <= 0:
print('nothing to peek')
return
print(self.top.value)
return self.top.value


class Queue:

def __init__(self):
'''initializes a queue instance'''
self.front = None
self.rear = None
self.length = 0

def isEmpty(self):
return self.front == None

def enqueue(self, value):
'''appends value to front of queue'''

self.length += 1
new_node = Node(value)

if self.rear == None:
self.front = self.rear = new_node

return

self.rear.next = new_node
self.rear = new_node

def dequeue(self):
'''removes value from front of queue, if length is zero it returns the queue
and prints a message'''
self.length -= 1

if self.isEmpty():
self.queue = []
print('queue is empty')
return self.queue

temp = self.front
self.front = temp.next

if self.front == None:
self.rear = None

return str(temp.value)

def peek(self):
'''returns the first value in a queue'''
return self.front.value
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from stacks_and_queues import Node, Stack, Queue

def test_empty_stack():
test_stack = Stack()
assert isinstance(test_stack, Stack)

def test_push():
test_stack = Stack()
test_stack.push(1)
test_stack.push(2)
assert test_stack.top.value == 2

def test_muliple_nodes():
test_stack = Stack()
test_stack.push(1)
test_stack.push(2)
test_stack.push(3)
assert test_stack.length == 3

def test_pop():
test_stack = Stack()
test_stack.push(1)
test_stack.push(2)
test_stack.push(3)
popped = test_stack.pop()
assert popped == 3
assert test_stack.length == 2
assert test_stack.top.value == 2

def test_multipop():
test_stack = Stack()
test_stack.push(1)
test_stack.push(2)
test_stack.push(3)
test_stack.pop()
test_stack.pop()
test_stack.pop()
assert test_stack.length == 0
assert test_stack.bottom == None

def test_peek():
test_stack = Stack()
test_stack.push(1)
test_stack.push(2)
test_stack.push(3)

assert test_stack.peek() == 3

def test_enqueue():
q = Queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)

assert q.front.value == 1

def test_enqueue_multiple():
q = Queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)

assert q.length == 3

def test_dequeue():
q = Queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
q.dequeue()

assert q.length == 2
assert q.front.value == 2

def test_enqueue_empty_queue():
q = Queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
q.dequeue()
q.dequeue()
q.dequeue()

assert q.length == 0

def test_instantiate_empty():
q = Queue()

assert q.length == 0
assert q.front == None


def test_q_peek():
q = Queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)

assert q.peek() == 1
Binary file not shown.
76 changes: 76 additions & 0 deletions data_structures_and_algorithms/data_structures/tree/resd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
Tree Traversals (Inorder, Preorder and Postorder)
Unlike linear data structures (Array, Linked List, Queues, Stacks, etc) which have only one logical way to traverse them, trees can be traversed in different ways. Following are the generally used ways for traversing trees.



Depth First Traversals:
(a) Inorder (Left, Root, Right) : 4 2 5 1 3
(b) Preorder (Root, Left, Right) : 1 2 4 5 3
(c) Postorder (Left, Right, Root) : 4 5 2 3 1

Breadth First or Level Order Traversal : 1 2 3 4 5
Please see this post for Breadth First Traversal.

Inorder Traversal :

Algorithm Inorder(tree)
1. Traverse the left subtree, i.e., call Inorder(left-subtree)
2. Visit the root.
3. Traverse the right subtree, i.e., call Inorder(right-subtree)
Uses of Inorder
In case of binary search trees (BST), Inorder traversal gives nodes in non-decreasing order. To get nodes of BST in non-increasing order, a variation of Inorder traversal where Inorder traversal s reversed can be used.


Preorder Traversal :

Algorithm Preorder(tree)
1. Visit the root.
2. Traverse the left subtree, i.e., call Preorder(left-subtree)
3. Traverse the right subtree, i.e., call Preorder(right-subtree)
Uses of Preorder
Preorder traversal is used to create a copy of the tree. Preorder traversal is also used to get prefix expression on of an expression tree.

Postorder Traversal :

Algorithm Postorder(tree)
1. Traverse the left subtree, i.e., call Postorder(left-subtree)
2. Traverse the right subtree, i.e., call Postorder(right-subtree)
3. Visit the root.
Uses of Postorder
Postorder traversal is used to delete the tree. Please see the question for deletion of tree for details. Postorder traversal is also useful to get the postfix expression of an expression tree.


Time Complexity: O(n)
Complexity function T(n) — for all problem where tree traversal is involved — can be defined as:

T(n) = T(k) + T(n – k – 1) + c

Where k is the number of nodes on one side of root and n-k-1 on the other side.

Let’s do an analysis of boundary conditions

Case 1: Skewed tree (One of the subtrees is empty and other subtree is non-empty )

k is 0 in this case.
T(n) = T(0) + T(n-1) + c
T(n) = 2T(0) + T(n-2) + 2c
T(n) = 3T(0) + T(n-3) + 3c
T(n) = 4T(0) + T(n-4) + 4c

…………………………………………
………………………………………….
T(n) = (n-1)T(0) + T(1) + (n-1)c
T(n) = nT(0) + (n)c

Value of T(0) will be some constant say d. (traversing a empty tree will take some constants time)

T(n) = n(c+d)
T(n) = Θ(n) (Theta of n)

Case 2: Both left and right subtrees have equal number of nodes.

T(n) = 2T(|_n/2_|) + c

This recursive function is in the standard form (T(n) = aT(n/b) + (-)(n) )

Auxiliary Space : If we don’t consider size of stack for function calls then O(1) otherwise O(n).
Loading