Skip to content

Commit eb4da7e

Browse files
committed
Heap Sort and DFS
1 parent 9eb758a commit eb4da7e

File tree

9 files changed

+279
-0
lines changed

9 files changed

+279
-0
lines changed

hw3 - heap sort/hw3-report.pdf

450 KB
Binary file not shown.

hw3 - heap sort/hw3.pdf

41.9 KB
Binary file not shown.

hw3 - heap sort/hw3.py

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import random
2+
from time import time
3+
4+
5+
# Time Complexity: O(n log n) + O(n)
6+
# Space Complexity: O(log n) for recursive stack space of max_heapify
7+
def heap_sort(arr):
8+
n = len(arr)
9+
build_max_heap(arr, n) # O(n)
10+
for i in range(n - 1, 0, -1): # delete n elements one by one from top, heapify
11+
arr[i], arr[0] = arr[0], arr[i]
12+
max_heapify(arr, i, 0)
13+
14+
15+
# assumes part of heap is sorted: log(n)
16+
def max_heapify(arr, n, i): # log n
17+
maximum = i
18+
left = (2 * i) + 1
19+
right = (2 * i) + 2
20+
if left < n and arr[i] < arr[left]:
21+
maximum = left
22+
if right < n and arr[maximum] < arr[right]:
23+
maximum = right
24+
if maximum != i:
25+
arr[i], arr[maximum] = arr[maximum], arr[i]
26+
max_heapify(arr, n, maximum)
27+
28+
29+
# build max heap from unsorted array: O(n)
30+
def build_max_heap(arr, n):
31+
for i in range(n // 2 - 1, -1, -1):
32+
max_heapify(arr, n, i)
33+
34+
35+
# Time Complexity: O(n log n)
36+
# Space Complexity: O(log n) stack space for recursion + O(n) array after merge
37+
def merge_sort_recursive(numbers, low, high):
38+
if low < high:
39+
middle = (low + high) // 2
40+
merge_sort_recursive(numbers, low, middle)
41+
merge_sort_recursive(numbers, middle + 1, high)
42+
merge(numbers, low, middle, high)
43+
44+
45+
def merge(numbers, low, mid, high):
46+
i = low
47+
j = mid + 1
48+
sorted_nums = []
49+
while i <= mid and j <= high:
50+
if numbers[i] < numbers[j]:
51+
sorted_nums.append(numbers[i])
52+
i += 1
53+
else:
54+
sorted_nums.append(numbers[j])
55+
j += 1
56+
57+
while i <= mid:
58+
sorted_nums.append(numbers[i])
59+
i += 1
60+
while j <= high:
61+
sorted_nums.append(numbers[j])
62+
j += 1
63+
numbers[low:high + 1] = sorted_nums[:]
64+
65+
66+
# Time Complexity: O(n log n); worst case O(n^2) when list is already sorted
67+
# Space Complexity: O(log n) stack space for recursion; O(n) stack in worst case
68+
def quick_sort_recursive(numbers, low, high):
69+
if low >= high:
70+
return
71+
partition_index = partition(numbers, low, high)
72+
quick_sort_recursive(numbers, low, partition_index - 1)
73+
quick_sort_recursive(numbers, partition_index, high)
74+
75+
76+
def partition(numbers, low, high):
77+
pivot = numbers[(low + high) // 2] # Take middle element as pivot
78+
while low <= high:
79+
while numbers[low] < pivot:
80+
low += 1
81+
while numbers[high] > pivot:
82+
high -= 1
83+
if low <= high:
84+
numbers[low], numbers[high] = numbers[high], numbers[low]
85+
# swap(numbers, low, high)
86+
low += 1
87+
high -= 1
88+
return low # new partition position, every left is smaller than pivot, every right is bigger than pivot
89+
90+
91+
if __name__ == '__main__':
92+
merge_time = []
93+
quick_time = []
94+
heap_time = []
95+
xs = []
96+
for x in range(100, 10100, 100):
97+
xs.append(x)
98+
# nums_merge = list(range(1, x + 1))
99+
nums_merge = random.sample(range(1, 10100), x)
100+
# print(nums_merge)
101+
nums_quick = nums_merge[:]
102+
nums_heap = nums_merge[:]
103+
104+
start_time = time()
105+
merge_sort_recursive(nums_merge, 0, len(nums_merge) - 1)
106+
time_taken = time() - start_time
107+
merge_time.append(time_taken * 1000)
108+
109+
start_time = time()
110+
quick_sort_recursive(nums_quick, 0, len(nums_quick) - 1)
111+
time_taken = time() - start_time
112+
quick_time.append(time_taken * 1000)
113+
114+
start_time = time()
115+
heap_sort(nums_heap)
116+
time_taken = time() - start_time
117+
heap_time.append(time_taken * 1000)
118+
119+
for y in range(len(merge_time)):
120+
print(xs[y], merge_time[y], quick_time[y], heap_time[y])

hw3 - heap sort/kth_in_two_sorted.py

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
def find_kth(list1, list2, start1, end1, start2, end2, k):
2+
# Edge case 1: k greater than size of list1 + list2
3+
if k > len(list1) + len(list2):
4+
print("Invalid k: not in range of combined lists")
5+
return -1
6+
7+
# Edge case 2: If list1 is exhausted, kth element is the kth in list2
8+
if start1 > end1:
9+
return list2[start2 + k - 1]
10+
11+
# Edge case 3: If list2 is exhausted, kth element is the kth in list1
12+
if start2 > end2:
13+
return list1[start1 + k - 1]
14+
15+
# Core Logic: calculate mid elements of list1 and list2
16+
mid1 = (start1 + end1) // 2
17+
mid2 = (start2 + end2) // 2
18+
19+
# if k is less than number of elements in list1[start1...mid1] and list2[start2...mid2] combined,
20+
# the kth element lies in range and so we halve search space by manipulating mids, moving to right halves
21+
if (mid1 - start1 + 1 + mid2 - start2 + 1) > k:
22+
if list1[mid1] > list2[mid2]: # get rid of right half of list1
23+
print("computation")
24+
return find_kth(list1, list2, start1, mid1 - 1, start2, end2, k)
25+
else: # get rid of right half of list2
26+
print("computation")
27+
return find_kth(list1, list2, start1, end1, start2, mid2 - 1, k)
28+
29+
# k is not within the range of combined list1[start1...mid1] and list2[start2...mid2],
30+
# we shift the search space by halving lists and manipulating k
31+
else:
32+
if list1[mid1] > list2[mid2]: # move to right half of list2, change k by factor of shift
33+
print("computation")
34+
return find_kth(list1, list2, start1, end1, mid2 + 1, end2, k - (mid2 - start2 + 1))
35+
else: # move to right half of list1, change k by factor of shift
36+
print("computation")
37+
return find_kth(list1, list2, mid1 + 1, end1, start2, end2, k - (mid1 - start1 + 1))
38+
39+
40+
if __name__ == '__main__':
41+
array1 = [1, 3, 5, 7, 9, 11, 12]
42+
array2 = [4, 5, 6]
43+
44+
# array1 = [10, 30, 40, 50]
45+
# array2 = [30, 50, 100, 110]
46+
print(find_kth(array1, array2, 0, 0, len(array1) - 1, len(array2) - 1, 2))
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
def find_kth(list1, list2, k):
2+
if k > len(list1) + len(list2):
3+
print("k greater than combined list's length")
4+
return -1
5+
if not list1:
6+
return list2[k - 1]
7+
elif not list2:
8+
return list1[k - 1]
9+
elif k == 1:
10+
return min(list1[0], list2[0])
11+
12+
mid1 = min(len(list1), k // 2)
13+
mid2 = min(len(list2), k // 2)
14+
print(mid1, mid2)
15+
if list1[mid1 - 1] < list2[mid2 - 1]:
16+
return find_kth(list1[mid1:], list2, k - mid1)
17+
return find_kth(list1, list2[mid2:], k - mid2)
18+
19+
20+
if __name__ == '__main__':
21+
array1 = [1, 3, 5, 7, 9, 11, 12]
22+
array2 = [4, 5, 6]
23+
24+
# array1 = [10, 30, 40, 50]
25+
# array2 = [30, 50, 100, 110]
26+
print(kth_element(array1, array2, 0, 0, len(array1) - 1, len(array2) - 1, 2))

hw4 - n queens/hw4-report.pdf

856 KB
Binary file not shown.

hw4 - n queens/hw4.pdf

30.5 KB
Binary file not shown.

hw4 - n queens/hw4.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import time
2+
from itertools import permutations
3+
4+
5+
def e_queens(n, solutions): # exhaustive search
6+
# Generate all possible permutations representing Q1->index 0, Q2->index1, ..., Qn->index n - 1
7+
for perm in permutations(range(n)):
8+
if is_valid_perm(perm, n): # Check if attack conditions are met
9+
solutions.append(perm)
10+
11+
12+
def is_valid_perm(permutation, n):
13+
for row_id in range(n): # for each queen
14+
for i in range(row_id): # compare queen at row_id's placement with the other columns
15+
if abs(permutation[i] - permutation[row_id]) == row_id - i: # diagonal clash check
16+
return False
17+
return True
18+
19+
20+
def b_queens(row, n, placement, solutions): # backtrack search
21+
if row == n: # when row reaches n, we have placed all the n queens without any attack conflicts
22+
solutions.append(placement[:])
23+
else:
24+
for column in range(n): # explore n columns for row'th queen
25+
placement.append(column)
26+
# print(placement)
27+
if is_valid(placement): # check if the row'th queen placed at column attacks already placed queens
28+
b_queens(row + 1, n, placement, solutions)
29+
placement.pop() # done exploring column'th position
30+
31+
32+
def is_valid(placement):
33+
row_id = len(placement) - 1
34+
# 0 to row_id - 1 queens are placed without attack conflicts
35+
for i in range(row_id): # compare newly placed row_id'th queen with already placed queens for attacks
36+
diff = abs(placement[i] - placement[row_id])
37+
if diff == 0 or diff == row_id - i: # same column clash or diagonal clash
38+
return False
39+
return True
40+
41+
42+
if __name__ == '__main__':
43+
N = 8
44+
for i in range(1, N + 1):
45+
solutions_e_queens = []
46+
start_e = time.time()
47+
e_queens(i, solutions_e_queens)
48+
e_time = (time.time() - start_e)
49+
50+
solutions_b_queens = []
51+
start_b = time.time()
52+
b_queens(0, i, [], solutions_b_queens)
53+
b_time = (time.time() - start_b)
54+
print("{}\t {}\t {}\t {}\t {}".format(i, len(solutions_e_queens), len(solutions_b_queens), e_time, b_time))

hw4 - n queens/n-queens.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import time
2+
3+
4+
def b_queens(row, n, placement, solutions): # backtrack search
5+
if row == n: # when row reaches n, we have placed all the n queens without any attack conflicts
6+
if is_valid(placement):
7+
solutions.append(placement)
8+
placement = []
9+
else:
10+
for column in range(n): # explore n columns for row'th queen
11+
placement.append(column)
12+
print(placement)
13+
b_queens(row + 1, n, placement, solutions)
14+
15+
16+
def is_valid(placement):
17+
row_id = len(placement) - 1
18+
# 0 to row_id - 1 queens are placed without attack conflicts
19+
for i in range(row_id): # compare newly placed row_id'th queen with already placed queens for attacks
20+
diff = abs(placement[i] - placement[row_id])
21+
if diff == 0 or diff == row_id - i: # same column clash or diagonal clash
22+
return False
23+
return True
24+
25+
26+
if __name__ == '__main__':
27+
N = 4
28+
for i in range(1, N + 1):
29+
solutions_b_queens = []
30+
start_b = time.time()
31+
b_queens(0, i, [], solutions_b_queens)
32+
b_time = (time.time() - start_b)
33+
print("{}\t {}\t {}".format(i, len(solutions_b_queens), b_time))

0 commit comments

Comments
 (0)