Add new course

This commit is contained in:
AndrewTrieu
2023-01-08 18:43:01 +02:00
parent 75313f3f05
commit 6f5c7f67b4
49 changed files with 835669 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
<!doctype html>
<html>
<head>
<title>Task description: Open Hashing</title>
<meta charset="utf-8">
</head>
<body>
<div>
<h4>1. Implementing the Hash Table</h4>
</div>
<div>Create your own hash table that uses open hashing in Python. Each slot of the hash table contains a linked
structure where the data (keys) are stored. The hash system must include search, insert, and delete operations. The hash table must be able to store both integer (<em>int</em>)
and string (<em>str</em>) values. That means that also all the operations/methods (search, insert, and delete) need to work with both data types. You can decide or design the hash function by yourself.<br></div>
<br>
<div>Consider following things when you are creating the hash table:</div>
<ul>
<ul>
<li>The size of the hash table is <strong>fixed</strong>. That means that after initializing the size of the table must stay the same.</li><li>You must implement the linked structure where the data is stored by yourself.<br></li><li>Choose your hashing function wisely because it must work efficiently with very large hash tables.
A Good start is the string folding. Be as creative you want but be prepared to explain how it works!</li>
<li>Document your code!</li>
</ul>
</ul>
<div><div>Save the code of your new data structure as <strong>hash_1.py</strong></div><br></div><div>Answer to the following essay questions:</div>
<ol>
<li>Present the structure of your hash table.</li>
<li>What hashing function did you choose and why?</li>
<li>What (including required) methods your hash table has and explain briefly how do they work?</li>
</ol><br>
<div>
<h4>2. Testing and Analyzing the Hash Table</h4>
</div>
<div>Create a Python program: <strong>hash_2.py</strong>:</div>
<ol>
<li>Create a new hash table of size \(3\). Add items <strong>12, 'hashtable', 1234, 4328989, 'BM40A1500', -12456, 'aaaabbbbcccc'</strong>
to the hash table. Present the structure of the hash table each time when a new value is added.</li>
<li>Now try to find values <strong>-12456, 'hashtable', 1235</strong>. Print out the results.</li>
<li>Remove values <strong>'BM40A1500', 1234, 'aaaabbbbcccc'</strong>. Present the final structure of the hash table.</li>
</ol>
<div>Answer to the following essay questions:</div>
<ol>
<li>What is the running time of adding a new value in your hash table and why?</li>
<li>What is the running time of finding a new value in your hash table and why?</li>
<li>What is the running time of removing a new value in your hash table and why?</li>
</ol>
<div>Use \(\Theta\) notation. Consider what factors influence the running time of the methods.</div>
<br>
<br>
<div>
<h4>3. The Pressure Test</h4>
</div>
<div>Let's put the hash table in a real use. The text file <a href="data/words_alpha.txt"><em>words_alpha.txt</em></a> (source: https://github.com/dwyl/english-words/)
contains \(370105\) English (and not so English) words. The text file
<a href="data/kaikkisanat.txt"><em>kaikkisanat.txt</em></a> (source: https://github.com/hugovk/everyfinnishword) contains \(93086\) Finnish words. Your task is to find all words from <em>kaikkisanat.txt</em> that are also in <em>words_alpha.txt</em> (exact matches).
</div>
<br>
<div>Create a new Python file <strong>hash_3_1.py</strong>:</div>
<ol>
<li>Create a new hash table of size \(10000\).</li>
<li>Read all words from <em>words_alpha.txt</em> and store them to your hash table.</li>
<li>While reading words from <em>kaikkisanat.txt</em> check how many of them can you find from the hash table
and print out the final result.</li>
</ol>
<div>Measure the runtime for each step. Tabulate the results
as follows:
</div>
<br>
<table frame="grey" cellspacing="1" cellpadding="0" border="1">
<tbody>
<tr>
<th>Process</th>
<th>Time (s)</th>
</tr>
<tr>
<td>Initializing the hash table</td>
<td></td>
</tr>
<tr>
<td>Adding the words</td>
<td></td>
</tr>
<tr>
<td>Finding the common words</td>
<td></td>
</tr>
</tbody>
</table>
<br>
<div>How does your hash table stand against a linear array? Repeat the previous test,
but this time store the words from <em>words_alpha.txt</em> to a <em>list</em> instead of the hash table. Save your code as <strong>hash_3_2.py</strong><br></div>
<div><br></div><div>Answer to the following essay questions:</div>
<ol>
<li>Which data structure was faster in adding the words from the file and why?</li>
<li>In which data structure was the search faster and why?</li>
<li>Are you able to make the test program in <strong>hash_3_1.py</strong> faster (even slight improvements)?
<ul>
<li>Try to change the size of the hash table.</li>
<li>How well is the data distributed in the hash table?</li>
</ul>
</li>
</ol>
<br>
<p>Provide your answers to all the essay questions in a single PDF file. You can use the template below:</p><a href="data/PA_essay_template.odt?time=1665307225135">
</a><p><a href="data/PA_essay_template.odt?time=1665398460962">PA_essay_template.odt</a><br></p>
</body>
</html>

View File

@@ -0,0 +1,128 @@
<!doctype html>
<html>
<head>
<title>Week 1 Programming Assignments (9 points)</title>
<meta charset="utf-8">
</head>
<body>
<div>
<h4><strong> Assignment 1.1: Insertion Sort</strong> (3 points)</h4>
</div>
<p></p>
<div> Following pseudo code sorts an array of integers. Command swap switches values of two variables. </div>
<p></p>
<pre>function isort(A)
for i = 1 to size(A)-1
j = i-1
while (j &gt;= 0) and (A[j] &gt; A[j+1])
swap(A[j], A[j+1])
j = j-1
return
</pre>
<p></p>
<div> Create the following function in Python: </div>
<ul>
<ul>
<li> <strong>isort(A: list)</strong>: sorts a given list of integers. Implement the pseudo code to this function. </li>
</ul>
</ul>
<p></p>
<div> Limits: </div>
<ul>
<ul>
<li> the maximum length of the list is \(10^3\) </li>
<li> each integer is between \(1...10^3\) </li>
</ul>
</ul>
<div>A code template with an example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>def isort(A):
# TODO
if __name__ == "__main__":
A = [4, 3, 6, 2, 9, 7, 1, 8, 5]
isort(A)
print(A) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
</pre>
</div>
<p></p>
<div> Submit your solution in CodeGrade as <strong>isort.py</strong>. </div>
<br>
<br>
<div>
<h4><strong> Assignment 1.2 Prime Numbers</strong>&nbsp;<span>(3 points)</span></h4>
</div>
<p></p>
<div> For the background read the first paragraph of this article: <a href="https://en.wikipedia.org/wiki/Prime_number">https://en.wikipedia.org/wiki/Prime_number</a></div>
<p></p>
<div> Given a number \(N\) how many prime numbers are less or equal to \(N\)? For example if \(N = 7\) there are four prime numbers: \(2\), \(3\), \(5\) and \(7\) (note that 1 is not a prime number). </div>
<p></p>
<div> Create the following function in Python: </div>
<ul>
<ul>
<li> <strong>primes(N: int)</strong>: returns the numbers of primes that are less or equal to \(N\) </li>
</ul>
</ul>
<div> Limits: \(1 \leq N \leq 10^5\) </div>
<p></p>
<div><div>A code template with an example program: </div></div>
<p></p>
<div style="border:2px solid black">
<pre>def primes(N):
# TODO
if __name__ == "__main__":
print(primes(7)) # 4
print(primes(15)) # 6
print(primes(50)) # 15
</pre>
</div>
<p></p>
<div> Submit your solution in CodeGrade as <strong>primes.py</strong>.</div>
<br>
<br>
<div>
<h4><strong> Assignment 1.3: Is it a Triangle?</strong> (3 points)</h4>
</div>
<p></p>
<div> Three integers \(a\), \(b\) and \(c\) presents the side lenghts of a triangle. Can you build any triangle from those three sides?</div>
<p></p>
<div> For example:</div>
<ol>
<li> sides \(3\), \(4\) and \(5\) makes a right angle triangle </li>
<li> sides \(5\), \(5\) and \(3\) makes an isosceles triangle </li>
<li> sides \(7\), \(3\) and \(3\) doesn't make a triangle </li>
<li> sides \(4\), \(3\) and \(-1\) doesn't make a triangle </li>
</ol>
<div> Create the following function in Python:</div>
<ul>
<ul>
<li><strong>triangle(a: int, b: int, c: int)</strong>: returns a boolean <strong>True</strong> if triangle can be built, <strong>False</strong> if not </li>
</ul>
</ul>
<div><div>A code template with an example program: </div></div>
<p></p>
<div style="border:2px solid black">
<pre>def triangle(a, b, c):
# TODO
if __name__ == "__main__":
print(triangle(3, 5, 4)) # True
print(triangle(-1, 2, 3)) # False
print(triangle(5, 9, 14)) # False
print(triangle(30, 12, 29)) # True
</pre>
</div>
<p></p>
<div> Submit your solution in CodeGrade as <strong>triangle.py</strong>.</div>
</body>
</html>

View File

@@ -0,0 +1,14 @@
def isort(A):
for i in range(1, len(A)):
j = i-1
while (j >= 0) and (A[j] > A[j+1]):
temp = A[j]
A[j] = A[j+1]
A[j+1] = temp
j = j-1
if __name__ == "__main__":
A = [4, 3, 6, 2, 9, 7, 1, 8, 5]
isort(A)
print(A) # [1, 2, 3, 4, 5, 6, 7, 8, 9]

View File

@@ -0,0 +1,12 @@
def primes(N):
a = 0
for num in range(2, N+1):
if all(num % i != 0 for i in range(2, num)):
a += 1
return a
if __name__ == "__main__":
print(primes(7)) # 4
print(primes(15)) # 6
print(primes(50)) # 15

View File

@@ -0,0 +1,12 @@
def triangle(a, b, c):
if (a+b) > c and (a+c) > b and (b+c) > a:
return True
else:
return False
if __name__ == "__main__":
print(triangle(3, 5, 4)) # True
print(triangle(-1, 2, 3)) # False
print(triangle(5, 9, 14)) # False
print(triangle(30, 12, 29)) # True

View File

@@ -0,0 +1,103 @@
<!doctype html>
<html>
<head>
<title>Week 10 Programming Assignments (8 points)</title>
<meta charset="utf-8">
</head>
<body>
<div>
<p><strong><span class="" style="color: rgb(239, 69, 64);">Note: both assignments for this week use the graph class implemented in Programming Assignment 9.1.</span></strong></p><h4><strong>Assignment 10.1: Floyd's Algorithm</strong> (4 points)</h4>
</div>
<div>To find the shortest distance between all pair of vertices the best solution is to use Floyd's algorithm.</div>
<br>
<div>Create a function <strong>floyd(graph: Graph)</strong> in Python. The function takes a Graph object as an input value and returns a \(V \times V\) matrix
(\(V\) sized list of \(V\) sized lists) containing distances between all vertex pairs (\(v_i,v_j\)). The function must work for both directed and undirected graphs.</div>
<br>
<div>A code template with an example program for the directed graph below:</div>
<p></p>
<!-- Add other path in Moodle! -->
<img src="data/examplegraph.png" alt="Example Graph" class="img-responsive atto_image_button_text-bottom" width="306" height="239">
<p></p>
<div style="border:2px solid black">
<pre>from graph import Graph
def floyd(graph):
# TODO
if __name__ == "__main__":
matrix = [
# 0 1 2 3 4 5
[0, 0, 7, 0, 9, 0], # 0
[0, 0, 0, 0, 0, 0], # 1
[0, 5, 0, 1, 0, 2], # 2
[6, 0, 0, 0, 0, 2], # 3
[0, 0, 0, 0, 0, 1], # 4
[0, 6, 0, 0, 0, 0] # 5
]
graph = Graph(matrix)
D = floyd(graph)
for i in range(6):
for j in range(6):
print(f"{D[i][j]:2d}", end=" ")
print()
# 0 12 7 8 9 9
# 0 0 0 0 0 0
# 7 5 0 1 16 2
# 6 8 13 0 15 2
# 0 7 0 0 0 1
# 0 6 0 0 0 0
</pre>
</div>
<p></p>
<div>Submit your solution in CodeGrade as <strong>floyd.py</strong> including your graph class in <strong>graph.py</strong>.</div>
<br>
<br>
<div>
<h4><strong>Assignment 10.2: Kruskal's Algorithm</strong> (4 points)</h4>
</div>
<div>Last week we constructed a graph with the shortest paths starting from a given vertex using Dijkstra's algorithm. Although the shortest -path graph might also be a minimal cost spanning tree (MCST), this is not guaranteed. Therefore, it is better to use an algorithm designed to produce MCST. Here we implement Kruskal's algorithm.</div>
<br>
<div>Create a function <strong>kruskal(graph: Graph)</strong> in Python. The function takes a Graph object as an input value and
returns a new Graph object that has only the edges that constructs the minimum spanning tree computed by Kruskal's algorithm. The created graph is undirected and
you can assume that the original graph is undirected too.</div>
<br>
<div>For example the on the left we have the original undirected graph and on the right we have a graph that the function produces.</div>
<br>
<p><img src="data/kruskal.png" alt="Example Graph" class="img-responsive atto_image_button_text-bottom" width="647" height="233"><br>
</p><br>
<div></div>A code template with an example program for the graph above:<br><br>
<div style="border:2px solid black">
<pre>from graph import Graph
def kruskal(graph):
# TODO
if __name__ == "__main__":
matrix = [
# 0 1 2 3 4 5
[0, 0, 7, 6, 9, 0], # 0
[0, 0, 5, 0, 0, 6], # 1
[7, 5, 0, 1, 0, 2], # 2
[6, 0, 1, 0, 0, 2], # 3
[9, 0, 0, 0, 0, 1], # 4
[0, 6, 2, 2, 1, 0] # 5
]
graph = Graph(matrix)
graph.bf_print(0) # 0 2 3 4 1 5
mst = kruskal(graph)
mst.bf_print(0) # 0 3 2 1 5 4
</pre>
</div>
<br>
<div>Submit your solution in CodeGrade as <strong>kruskal.py</strong> including your graph class in <strong>graph.py</strong>.</div>
<br>
</body>
</html>

View File

@@ -0,0 +1,45 @@
from sys import maxsize
from graph import Graph
def floyd(graph: Graph):
distance = graph.matrix
for x in range(graph.V):
for y in range(graph.V):
if x != y and distance[x][y] == 0:
distance[x][y] = maxsize
for k in range(graph.V):
for i in range(graph.V):
for j in range(graph.V):
distance[i][j] = min(
distance[i][j], distance[i][k] + distance[k][j])
for x in range(graph.V):
for y in range(graph.V):
if x != y and distance[x][y] == maxsize:
distance[x][y] = 0
return distance
if __name__ == "__main__":
matrix = [
# 0 1 2 3 4 5
[0, 0, 7, 0, 9, 0], # 0
[0, 0, 0, 0, 0, 0], # 1
[0, 5, 0, 1, 0, 2], # 2
[6, 0, 0, 0, 0, 2], # 3
[0, 0, 0, 0, 0, 1], # 4
[0, 6, 0, 0, 0, 0] # 5
]
graph = Graph(matrix)
D = floyd(graph)
for i in range(6):
for j in range(6):
print(f"{D[i][j]:2d}", end=" ")
print()
# 0 12 7 8 9 9
# 0 0 0 0 0 0
# 7 5 0 1 16 2
# 6 8 13 0 15 2
# 0 7 0 0 0 1
# 0 6 0 0 0 0

View File

@@ -0,0 +1,50 @@
from graph import Graph
def kruskal(graph: Graph):
matrix = graph.matrix
V = graph.V
edges = []
for i in range(V):
for j in range(V):
if matrix[i][j] > 0:
edges.append((i, j, matrix[i][j]))
edges.sort(key=lambda item: item[2])
subsets = []
for i in range(V):
subsets.append([i])
result = []
for edge in edges:
subset1 = None
subset2 = None
for subset in subsets:
if edge[0] in subset:
subset1 = subset
if edge[1] in subset:
subset2 = subset
if subset1 != subset2:
result.append(edge)
subsets.remove(subset1)
subsets.remove(subset2)
subsets.append(subset1 + subset2)
new_matrix = [[0] * V for _ in range(V)]
for edge in result:
new_matrix[edge[0]][edge[1]] = edge[2]
new_matrix[edge[1]][edge[0]] = edge[2]
return Graph(new_matrix)
if __name__ == "__main__":
matrix = [
# 0 1 2 3 4 5
[0, 0, 7, 6, 9, 0], # 0
[0, 0, 5, 0, 0, 6], # 1
[7, 5, 0, 1, 0, 2], # 2
[6, 0, 1, 0, 0, 2], # 3
[9, 0, 0, 0, 0, 1], # 4
[0, 6, 2, 2, 1, 0] # 5
]
graph = Graph(matrix)
graph.bf_print(0) # 0 2 3 4 1 5
mst = kruskal(graph)
mst.bf_print(0) # 0 3 2 1 5 4

View File

@@ -0,0 +1,112 @@
<!doctype html>
<html>
<head>
<title>Week 11 Programming Assignments (8 points)</title>
<meta charset="utf-8">
</head>
<body>
<div>
<p><strong style="color: rgb(0, 0, 0); font-size: 18px;">Assignment 11.1: Traveling salesman problem and branch-and-bound</strong><span style="color: rgb(0, 0, 0); font-size: 18px;">&nbsp;(4 points)</span><br></p>
</div>
<div>In traveling salesman problem, the task is to find the shortest possible route that visits each city exactly once and returns to the origin city. The problem is known to be NP-hard. See <a href="https://moodle.lut.fi/mod/page/view.php?id=699882">Sec. 11.4.4 of the background material</a> for the introduction to the problem.&nbsp;</div>
<div><br></div>
<div>No efficient (polynomial time) algorithm is known for the problem. With a small amount of cities we can test all the possible permutations using backtracking and select the shortest route. However, the computation time quickly becomes unbearable when the amount of cities increases. Using <a href="https://moodle.lut.fi/mod/page/view.php?id=698497" target="_blank">branch-and-bound</a> we can reduce the computation time remarkably by reducing the number of solutions to be investigated. This approach still always produce the optimal solution, but can be used with a larger number of cities than the simple backtracking.&nbsp;</div>
<div></div><br>
<div>Create a Python function <strong>salesman(city_map: list)</strong>&nbsp;that solves the traveling salesman problem using branch-and-bound algorithm. The function takes a distance matrix (city_map[i][j] tells the distance between \(i\)th and \(j\)th city) of the cities as an input value and returns a order of the cities traversed in a list.
Cities are labeled as integers starting from zero and the traverse always starts from the first city (\(0\)th city).</div>
<br><strong>Target:</strong> <br>
<ul>
<li>The function finds the optimal route with 10 cities in 2 seconds on CodeGrade (2 points)</li>
<li>The function finds the optimal route with 10 cities in 0.5 seconds on CodeGrade (4 points)</li>
</ul>
<div>Note, that testing all permutations with simple backtracking is too slow. In order the achieve either of the targets, the function needs to use branch-and-bound. To obtain 4 points, a more accurate estimate for the lower bound is needed.</div>
<div><br></div>
<div>A code template with an example program:</div><br>
<div style="border:2px solid black">
<pre>def salesman(city_map):
# TODO
if __name__ == "__main__":
cost = 0
city_map = [
# 0 1 2 3 4
[ 0, 12, 19, 16, 29], # 0
[12, 0, 27, 25, 5], # 1
[19, 27, 0, 8, 4], # 2
[16, 25, 8, 0, 14], # 3
[29, 5, 4, 14, 0] # 4
]
path = salesman(city_map)
for i in range(len(city_map)):
cost += city_map[path[i]][path[i+1]]
print(path) # [0, 1, 4, 2, 3, 0]
print(cost) # 45
</pre>
</div>
<p></p>
<div>Submit your solution in CodeGrade as <strong>salesman.py</strong>.</div>
<br>
<br>
<div>
<h4><strong>Assignment 11.2: The bin packing problem and approximation algorithms</strong>&nbsp;(4 points)</h4>
</div>
<div>When the size of the problem increases even the more sophisticated exact algorithms (e.g. branch-and-bound and dynamic programming) become insufficient. In such cases, we typically need to settle for approximation algorithms. Such algorithms do not (always) provide the optimal solutions, but usually provide a good solution fast.</div>
<div><br></div>
<div>
<div><strong>Bin packing problem:</strong> given a set of \(n\) items with sizes \([s_1, s_2, s_3, … , s_n]\), find a way to pack the items to bins with size \(S\) so that the total amount of bins is minimized.</div>
<div><br></div>
<div>Design and implement an approximation algorithm for the bin packing problem. The algorithm must be able to find a solution for very large set of items. Therefore, brute-force approach or other algorithms that always find the optimal solution are not suitable. You will get points based on how close the solutions your algorithm produces are to the optimal solutions.&nbsp;</div>
<div><br></div>
<div>Create a function <strong>binpack(items: list, S: int)</strong> in Python. The function takes list of items and a maximum bin size \(S\) as an input value and returns a list of all bins. Each bin is a list of items.</div>
<div><br></div>
<div><strong>Target:&nbsp;</strong></div>
<div>
<ul>
<li>the algorithm is able to find an (approximate) solution in less than \(1\) seconds when \(n \leq 1000\), and<br></li>
<li>the solution (number of bins) is at most 50% larger than the optimal amount of bins (1 point)<br></li>
<li>the solution (number of bins)&nbsp;is at most 5% larger than the optimal amount of bins (2 points)<br></li>
<li>the solution (number of bins)&nbsp;is at most 1% larger than the optimal amount of bins (4 points)</li>
</ul>
</div>
<div>Note that to get the points, you do not need to find a solution that has a theoretical worst case of 1% larger than the optimum. It is enough that you obtain good enough solution for the cases that are used in CodeGrade. Even quite simple solutions produce almost always much better solution than the theoretical worst case.</div>
<div><br></div>
<div></div>A code template with an example program:<br><br>
<div style="border:2px solid black">
<pre>def binpack(items, S):
# TODO
if __name__ == "__main__":
items = [9, 3, 3, 6, 10, 4, 6, 8, 6, 3]
B = 10
bins = binpack(items, B)
for i in range(len(bins)):
print(f"bin {i+1}: {bins[i]}")
# A possible output:
# bin 1: [9]
# bin 2: [3, 3, 4]
# bin 3: [6, 3]
# bin 4: [10]
# bin 5: [6]
# bin 6: [8]
# bin 7: [6]
</pre>
</div>
<br>
<div>Submit your solution in CodeGrade as <strong>binpack.py</strong>.</div>
<br>
</div>
</body>
</html>

View File

@@ -0,0 +1,143 @@
<!doctype html>
<html>
<head>
<title>Week 2 Programming assignments (9 points)</title>
<meta charset="utf-8">
</head>
<body>
<div>
<h4><strong> Assignment 2.1: Changes</strong> (3 points)</h4>
</div>
<p></p>
<div> An array of \(n\) number of integers must be modified so that no two consecutive integers are equal. The new value can be chosen arbitrarily. What is the minimum number of required changes? </div>
<p></p>
<div> For Example array \([1, 1, 2, 2, 2]\) requires 2 changes. Changed array can be e.g. \([1, <strong><span class="" style="color: rgb(51, 51, 51);">3</span></strong>, 2, <strong><span class="" style="color: rgb(51, 51, 51);">3</span></strong>, 2]\) (the changed integers are in bold).
</div>
<p></p>
<div> Create the following function in Python: </div>
<ul>
<ul>
<li> <strong>changes(A: list)</strong>: returns the minimum number of required changes
</li>
</ul>
</ul>
<div> Limits: </div>
<ul>
<ul>
<li> \(1 \leq n \leq 10^6\) </li>
<li> each integer is between \(1...10^3\) </li>
</ul>
</ul>
<div><strong> Target: Algorithm performs in \(\Theta(n)\) time. </strong></div>
<p></p>
<div>A code template with an example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>def changes(A):
# TODO
if __name__ == "__main__":
print(changes([1, 1, 2, 2, 2])) # 2
print(changes([1, 2, 3, 4, 5])) # 0
print(changes([1, 1, 1, 1, 1])) # 2 </pre>
</div>
<p></p>
<div><br> </div>
<div>Submit your solution in CodeGrade as <strong>changes.py</strong>. <br></div>
<div><br></div>
<p></p>
<p></p>
<div>
<h4><strong> Assignment 2.2: Bit Pairs</strong> (3 points)</h4>
</div>
<p></p>
<div> We are given a bit string which each character is either 0 or 1. Count the sum of each distance of bit pairs where both bits are 1. </div>
<p></p>
<div> For example a bit string 100101 has following distances </div>
<ul>
<ul>
<li> <strong>1</strong>00<strong>1</strong>01 (3) </li>
<li> <strong>1</strong>0010<strong>1</strong> (5) </li>
<li> 100<strong>1</strong>0<strong>1</strong> (2) </li>
</ul>
</ul>
<div> Therefore the sum of distances is \(3+5+2 = 10\). </div>
<p></p>
<div> Create the following function in Python:</div>
<ul>
<ul>
<li><strong>pairs(s: str)</strong>: returns the sum of distances </li>
</ul>
</ul>
<div> Limits: the maximum length of the bit string is \(10^5\) </div>
<p></p>
<div><strong> Target: Algorithm performs in \(\Theta(n)\) time. </strong></div>
<p></p>
<div>A code template with an example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>def pairs(s):
# TODO
if __name__ == "__main__":
print(pairs("100101")) # 10
print(pairs("101")) # 2
print(pairs("100100111001")) # 71
</pre>
</div>
<p></p>
<div> Submit your solution solution in CodeGrade as <strong>bitpairs.py</strong>.</div>
<div><br></div>
<p></p>
<p></p>
<div>
<h4><strong> Assignment 2.3: Split Lists&nbsp;</strong>(3 points)</h4>
</div>
<p></p>
<div>An array of \(n\) number of integers must be split in two sub arrays so that every integer of left sub array are smaller than every integer of right sub array. In how many points the array can be split in half? </div>
<p></p>
<div> For example array \([2, 1, 2, 5, 7, 6, 9]\) can be split in 3 ways: </div>
<ol>
<li>\([2, 1, 2]\) and \([5, 7, 6, 9]\)</li>
<li>\([2, 1, 2, 5]\) and \([7, 6, 9]\)</li>
<li>\([2, 1, 2, 5, 7, 6]\) and \([9]\)</li>
</ol>
<div> Create following function(s) in Python:</div>
<ul>
<ul>
<li><strong>split(A: list)</strong>: returns the number of possible splits </li>
</ul>
</ul>
<div> Limits: </div>
<ul>
<ul>
<li>\(1 \leq n \leq 10^5\)<br></li>
<li> each integer is between \(1...10^3\) </li>
</ul>
</ul>
<div><strong> Target: Algorithm performs in \(\Theta(n)\) time. </strong></div>
<p></p>
<div>A code template with an example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>def split(T):
# TODO
if __name__ == "__main__":
print(count([1,2,3,4,5])) # 4
print(count([5,4,3,2,1])) # 0
print(count([2,1,2,5,7,6,9])) # 3
print(count([1,2,3,1])) # 0</pre>
</div>
<p></p>
<div> Submit your solution solution in CodeGrade as <strong>split.py</strong>.</div>
</body>
</html>

View File

@@ -0,0 +1,13 @@
def pairs(s):
result = 0
loc = [x for x in range(len(s)) if s[x] == "1"]
for i in range(len(loc)):
result += loc[i]*(i) - loc[i]*(len(loc)-i-1)
return result
if __name__ == "__main__":
print(pairs("100101")) # 10
print(pairs("101")) # 2
print(pairs("100100111001")) # 71
print(pairs("0011110001110011010111"))

View File

@@ -0,0 +1,17 @@
def changes(A):
count = 0
i = 0
while i < len(A):
if i == len(A)-1:
break
else:
if A[i+1] == A[i]:
count += 1
i += 2
else:
i += 1
return count
if __name__ == "__main__":
print(changes([1, 2, 5, 5, 4, 2]))

View File

@@ -0,0 +1,25 @@
def split(T):
count = 0
max_from_left = T[0]
min_to_right = T[-1]
for i in range(1, len(T)):
max_from_left = max(max_from_left, T[i])
for i in range(len(T) - 2, -1, -1):
min_to_right = min(min_to_right, T[i])
for i in range(len(T) - 1):
if max_from_left < min_to_right:
count += 1
max_from_left = max(max_from_left, T[i])
min_to_right = min(min_to_right, T[i+1])
return count
if __name__ == "__main__":
print(split([1, 2, 3, 4, 5])) # 4
print(split([5, 4, 3, 2, 1])) # 0
print(split([2, 1, 2, 5, 7, 6, 9])) # 3
print(split([1, 2, 3, 1])) # 0

View File

@@ -0,0 +1,82 @@
<!doctype html>
<html>
<head>
<title>Week 3: Programming Assignments (8 points)</title>
<meta charset="utf-8">
</head>
<body>
<div>
<h4><strong>Assignment 3.1: Linked List </strong>(5 points)<strong><br></strong></h4>
</div>
<div>Implement the linked list data structure in Python. Create a class <strong>Node</strong> which stores the data and link to another Node class. Create also the class <strong>LinkedList</strong> which maintains the linked list created by Node classes and has the following methods:
</div>
<ul>
<ul>
<li><strong>append(data: object)</strong>: inserts a new Node containing the data to the end of the list.<br></li>
<li><strong>insert(data: object, index: int)</strong>: inserts a new Node containing the data to the position indicated by the index (the index of the first node is 0).</li>
<li><strong>delete(index: int)</strong>: deletes a node from the position indicated by the index.</li>
<li><strong>index(data: object)</strong>: search the Node containing the data and returns its index. Returns \(-1\) if not found.</li>
<li><strong>print()</strong>: prints the content of linked list (format: node1 -&gt; node2 -&gt; node3).</li>
</ul>
</ul>
<div>A code template with an example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>class Node:
# TODO
class LinkedList:
# TODO
if __name__ == "__main__":
L = LinkedList()
L.append(1)
L.append(3)
L.print() # 1 -&gt; 3
L.insert(10, 1)
L.insert(15, 0)
L.print() # 15 -&gt; 1 -&gt; 10 -&gt; 3
print(L.index(1)) # 1
L.delete(0)
L.print() # 1 -&gt; 10 -&gt; 3
</pre>
</div>
<p></p>
<div>Submit your solution in CodeGrade as <strong>linked.py</strong>.</div>
<div><br><br></div>
<div>
<h4><strong>Assignment 3.2: Binary Search </strong>(3 points)</h4>
</div>
<div>Implement the binary search algorithm in Python. Create a function <strong>search(A: list, item: int)</strong> which returns the index of the item in the list. If the item is not found the function returns \(-1\). You can assume that the list contains only
integers and it is always sorted.</div>
<div><br>Limits:</div>
<ul>
<ul>
<li>Integers in the list are between \(1...10^6\)</li>
<li>The maximum length of the list is \(10^6\)</li>
</ul>
</ul>
<div>Target: the algorithm performs in \(\Theta(\log(n))\) time.</div>
<p></p>
<div>A code template with an example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>def search(A: list, item: int):
# TODO
if __name__ == "__main__":
A = [1, 2, 3, 6, 10, 15, 22, 27, 30, 31]
print(search(A, 6)) # 3
print(search(A, 7)) # -1
print(search(A, 30)) # 8
</pre>
</div>
<div><br></div><div>Submit your solution in CodeGrade as <strong>binarysearch.py</strong>.</div>
</body>
</html>

View File

@@ -0,0 +1,20 @@
def search(A: list, item: int):
low = 0
high = len(A) - 1
mid = 0
while low <= high:
mid = (high + low) // 2
if A[mid] < item:
low = mid + 1
elif A[mid] > item:
high = mid - 1
else:
return mid
return -1
if __name__ == "__main__":
A = [1, 2, 3, 6, 10, 15, 22, 27, 30, 31]
print(search(A, 6)) # 3
print(search(A, 7)) # -1
print(search(A, 30)) # 8

View File

@@ -0,0 +1,97 @@
class Node:
def __init__(self, data=None):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def append(self, data):
new = Node(data)
if self.head is None:
self.head = new
return
last = self.head
while (last.next):
last = last.next
last.next = new
def print(self):
val = self.head
while val is not None:
if val.next is None:
print(val.data)
else:
print(val.data, end=" -> ")
val = val.next
def length(self):
temp = self.head
count = 0
while (temp != None):
count += 1
temp = temp.next
return count
def insert(self, data, index):
new = Node(data)
if (index == 0):
new.next = self.head
self.head = new
else:
temp = self.head
for i in range(1, index):
if (temp != None):
temp = temp.next
if (temp != None):
new.next = temp.next
temp.next = new
else:
print("\nThe previous node is null.")
def index(self, data):
temp = self.head
index = 0
while (temp != None):
if (temp.data == data):
return index
temp = temp.next
index += 1
return -1
def delete(self, index):
if (index == 0):
self.head = self.head.next
elif (index >= self.length()):
return
else:
temp = self.head
for i in range(1, index):
if (temp != None):
temp = temp.next
if (temp != None):
temp.next = temp.next.next
else:
print("\nThe previous node is null.")
if __name__ == "__main__":
L = LinkedList()
L.append(2)
L.append(3)
L.append(1)
L.append(4)
L.print() # 1 -> 3
L.insert(4, 3)
L.insert(1, 0)
L.insert(3, 2)
L.insert(2, 1)
L.print() # 15 -> 1 -> 10 -> 3
print(L.index(1)) # 1
L.delete(0)
L.delete(1)
L.delete(4)
L.delete(5)
L.print() # 1 -> 10 -> 3

View File

@@ -0,0 +1,106 @@
<!doctype html>
<html>
<head>
<title>Week 4: Programming Assignments (8 points)</title>
<meta charset="utf-8">
</head>
<body>
<div>
<h4><strong>Programming Assignments: Hashing</strong></h4>
</div>
<div>Both assignments are related to fixed sized hash tables. The hash tables store string (str) values.
The hash value (slot) is calculated with the following hash function for strings:</div>
<p></p>
<pre>procedure hash(data):
sum = 0
for i = 0 to N-1 do
sum += ascii(data[i])
return sum % X
</pre>
<p></p>
<div>where N is the length of the string (data), <span style="color: rgb(33, 37, 41); font-family: SFMono-Regular, Menlo, Monaco, Consolas, &quot;Liberation Mono&quot;, &quot;Courier New&quot;, monospace; font-size: 14px;">% </span>is the symbol for the mod operation and&nbsp;<span style="color: rgb(33, 37, 41); font-family: SFMono-Regular, Menlo, Monaco, Consolas, &quot;Liberation Mono&quot;, &quot;Courier New&quot;, monospace; font-size: 14px;">X </span>is a parameter of the hash table. The ascii value of a character can be calculated with the function <strong><a href="https://www.w3schools.com/python/ref_func_ord.asp">ord</a></strong> in Python.</div>
<br>
<br>
<div>
<h4><strong>Assignment 4.1: Linear Probing </strong>(4 points)</h4>
</div>
<div>Implement a fixed sized hash table in Python that uses <u>linear probing</u> for collision resolution.
Create a class <strong>HashLinear</strong> which has the table size \(M\) as a input value when a object is created. The class has following methods:</div>
<ul>
<ul>
<li><strong>insert(data: str)</strong>: inserts new data into the hash table, no duplicates</li>
<li><strong>delete(data: str)</strong>: removes data from the hash table</li>
<li><strong>print()</strong>: prints the content of the hash table (the data string in each slot separated with a space; skip empty slots; see the example below)</li>
</ul>
</ul>
<div>For hashing use \(X = M\).</div>
<p></p>
<div>A code template with an example program (the hash table has the size of \(M=8\)): </div>
<p></p>
<div style="border:2px solid black">
<pre>class HashLinear:
# TODO
if __name__ == "__main__":
table = HashLinear(8)
table.insert("BM40A1500")
table.insert("fOo")
table.insert("123")
table.insert("Bar1")
table.insert("10aaaa1")
table.insert("BM40A1500")
table.print() # 10aaaa1 BM40A1500 fOo 123 Bar1
table.delete("fOo")
table.delete("Some arbitary string which is not in the table")
table.delete("123")
table.print() # 10aaaa1 BM40A1500 Bar1
</pre>
</div>
<div><br></div><div>Submit your solution in CodeGrade as <strong>hashlinear.py</strong>.</div>
<br>
<br>
<div>
<h4><strong>Assignment 4.1: Bucket Hashing </strong>(4 points)</h4>
</div>
<div>Implement a fixed sized hash table in Python that uses <u>bucket hashing</u> for collision resolution.
Create a class <strong>HashBucket</strong> which has the table size \(M\) and number of equal sized buckets \(B\) as the input values when a object is created.
The hash table has a overflow array of size \(M\). The class has the following methods:</div>
<ul>
<ul>
<li><strong>insert(data)</strong>: inserts new data in the hash table, no duplicates</li>
<li><strong>delete(data)</strong>: removes data from the hash table</li>
<li><strong>print()</strong>: prints the content of the hash table and the overflow array (the data string in each slot followed by the data in the overflow array; slots separated with a space and empty slots skipped; see the example below)</li>
</ul>
</ul>
<div>For hashing use \(X = B\). Filling the buckets starts from the top and overflow values are appended to the end of the overflow array. </div>
<p></p>
<div>A code template with an example program (the hash table has the size of \(M=8\) and has \(B=4\) buckets):<br></div>
<p></p>
<div style="border:2px solid black"><pre>class HashBucket:
# TODO
if __name__ == "__main__":
table = HashBucket(8, 4)
table.insert("BM40A1500")
table.insert("fOo")
table.insert("123")
table.insert("Bar1")
table.insert("10aaaa1")
table.insert("BM40A1500")
table.print() # fOo BM40A1500 123 Bar1 10aaaa1
table.delete("fOo")
table.delete("Some arbitary string which is not in the table")
table.delete("123")
table.print() # BM40A1500 Bar1 10aaaa1
</pre>
</div>
<div><br></div><div>Submit your solution in CodeGrade as <strong>hashbucket.py</strong>.</div>
</body>
</html>

View File

@@ -0,0 +1,79 @@
def hash(S, size):
num = 0
for i in range(0, len(S)):
num += ord(S[i])
return num % size
# Hash table with bucket hashing and overflow array
# Class HashBucket which has the table size M and number of equal sized buckets B as the input values when a object is created. Size of one bucket is M/B. The hash table has a overflow array of size M.
class HashBucket:
def __init__(self, M, B):
self.M = M
self.B = B
self.table = [[] for i in range(0, M)]
self.overflow = []
# No duplicate strings are allowed
def insert(self, S):
h = hash(S, self.B)
if S in self.table[h]:
return
elif S in self.overflow:
return
elif len(self.table[h]) < self.M / self.B:
self.table[h].append(S)
else:
self.overflow.append(S)
def search(self, S):
h = hash(S, self.B)
if S in self.table[h]:
return True
elif S in self.overflow:
return True
else:
return False
def delete(self, S):
h = hash(S, self.B)
if S in self.table[h]:
self.table[h].remove(S)
elif S in self.overflow:
self.overflow.remove(S)
else:
print("String not found")
def print(self):
for i in range(0, self.M):
for j in range(0, len(self.table[i])):
print(self.table[i][j], end=' ')
for z in range(0, len(self.overflow)):
print(self.overflow[z], end=' ')
print()
# Main program
if __name__ == "__main__":
table = HashBucket(20, 4)
a = ["door", "billion", "how", "choice", "at", "husband", "truth", "song", "share", "develop", "Mr", "everybody", "common", "blood", "Democrat", "until", "stock", "southern", "song", "cover"
]
for i in a:
table.insert(i)
table.print()
b = ["song", "develop", "choice", "common", "until", "how", "billion", "blood", "door", "truth"
]
for i in b:
table.delete(i)
table.print()
table = HashBucket(8, 4)
a = ["dinner", "while", "call", "relate",
"be", "easy", "yourself", "decide"]
for i in a:
table.insert(i)
table.print()
b = ["relate", "call", "decide", "while"]
for i in b:
table.delete(i)
table.print()

View File

@@ -0,0 +1,75 @@
def hash(S, size):
num = 0
for i in range(0, len(S)):
num += ord(S[i])
return num % size
# Hash table with linear probing
class HashLinear:
def __init__(self, num):
self.table = ["DELETED"] * num
self.size = 0
self.num = num
def insert(self, S):
if self.search(S):
return False
h = hash(S, self.num)
while self.table[h] != "DELETED" and self.table[h] != None:
h = (h + 1) % self.num
self.table[h] = S
self.size += 1
return True
def search(self, S):
h = hash(S, self.num)
while self.table[h] != "DELETED":
if self.table[h] == S:
return True
h = (h + 1) % self.num
return False
def find(self, S):
h = hash(S, self.num)
while self.table[h] != "DELETED":
if self.table[h] == S:
return h
h = (h + 1) % self.num
return -1
def delete(self, S):
if not self.search(S):
return False
h = hash(S, self.num)
if self.table[h] != S:
while self.table[h] != S:
h = (h + 1) % self.num
self.table[h] = "DELETED"
self.table[h] = None
self.size -= 1
return True
def print(self):
for i in range(0, self.num):
if self.table[i] != "DELETED" and self.table[i] != None:
#print(i, self.table[i])
print(self.table[i], end=' ')
print()
def __str__(self):
return str(self.table)
if __name__ == "__main__":
table = HashLinear(20)
a = ["town", "rather", "short", "toward", "employee", "player", "toward", "the", "of", "college",
"in", "yes", "billion", "five", "wear", "last", "decade", "first", "training", "friend"]
for i in a:
table.insert(i)
b = ["employee", "of", "toward", "in", "player",
"town", "toward", "five", "rather", "yes"]
for i in b:
table.delete(i)
table.print()

View File

@@ -0,0 +1,121 @@
<!doctype html>
<html>
<head>
<title>Week 5: Programming Assignments (9 points)</title>
<meta charset="utf-8">
</head>
<body>
<div>
<h4><strong>Assignment 5.1: Binary Search Tree</strong> <span>(4 points)</span></h4>
</div>
<div>Implement a binary seach tree in Python. The tree stores integers (keys) only.
<p></p>
<div>Create following classes:
<ul>
<ul>
<li><strong>Node</strong> which stores the integer value (key) and links to its left and right child</li>
<li><strong>BST</strong> maintains the binary search tree built with Node classes. </li>
</ul>
</ul>
</div>
<div>Create following methods for class BTS:</div>
<ul>
<ul>
<li><strong>insert(key: int)</strong>: inserts a new key to the search tree, no duplicates.</li>
<li><strong>search(key: int)</strong>: searches the key from the search tree and returns boolean True if the value is found, False otherwise.</li>
<li><strong>preorder()</strong>: prints the content of the search tree in preorder. Implement the method using recursion.</li>
</ul>
</ul>
<p></p>
<div>A code template with an example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>class Node:
# TODO
class BST:
# TODO
if __name__ == "__main__":
Tree = BST()
keys = [5, 9, 1, 3, 7, 4, 6, 2]
for key in keys:
Tree.insert(key)
Tree.preorder() # 5 1 3 2 4 9 7 6
print(Tree.search(6)) # True
print(Tree.search(8)) # False
</pre>
</div>
<p></p>
<div>Submit your solution in CodeGrade as <strong>bintree.py</strong>.</div>
<br>
<br>
<div>
<h4><strong>Assignment 5.2: Removing a Node</strong> (3 points)</h4>
</div>
<div>Add new method <strong>remove(key: int)</strong> to the BST class. The function removes the node which has the given key value while maintaining the &nbsp;binary search tree property.</div>
<p></p>
<div>An example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>if __name__ == "__main__":
Tree = BST()
keys = [5, 9, 1, 3, 7, 4, 6, 2]
for key in keys:
Tree.insert(key)
Tree.preorder() # 5 1 3 2 4 9 7 6
Tree.remove(1)
Tree.preorder() # 5 3 2 4 9 7 6
Tree.remove(9)
Tree.preorder() # 5 3 2 4 7 6
Tree.remove(3)
Tree.preorder() # 5 2 4 7 6
</pre>
</div>
<p></p>
<div>Submit your expanded version of <strong>bintree.py</strong> in CodeGrade.</div>
<br>
<br>
<div>
<h4><strong>Assignment 5.3: Breadth-First Enumeration </strong>(2 points)</h4>
</div>
<div>Breadth-First enumeration presents the nodes of the search tree level by level (or depth) unlike preorder
enumeration which presents the left subtree completely before the right subtree.</div>
<div><br></div>
<div><img src="data/Animated_BFS.gif" alt="Breadth-first enumeration for a binary tree." width="187" height="175" class="img-fluid atto_image_button_text-bottom">&nbsp;</div>
<div><strong>Figure 1</strong><strong>:</strong> Breadth-First enumeration for a binary tree. Black: explored, grey: queued to be explored later on&nbsp;(source: <em>wikipedia.org</em>).&nbsp;</div>
<p></p>
<div>Add a new method <strong>breadthfirst()</strong> to the BST class which prints out the content of the search tree in breadth-first order.</div>
<p></p>
<div>An example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>if __name__ == "__main__":
Tree = BST()
keys = [5, 9, 1, 3, 7, 4, 6, 2]
for key in keys:
Tree.insert(key)
Tree.preorder() # 5 1 3 2 4 9 7 6
Tree.breadthfirst() # 5 1 9 3 7 2 4 6
</pre>
</div>
<p></p>
<div>Submit your expanded version of <strong>bintree.py</strong> in CodeGrade.</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,122 @@
class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
class BST:
def __init__(self):
self.root = None
def insert(self, key):
if self.root is None:
self.root = Node(key)
else:
self._insert(self.root, key)
def _insert(self, node, key):
if key < node.key:
if node.left is None:
node.left = Node(key)
else:
self._insert(node.left, key)
elif key > node.key:
if node.right is None:
node.right = Node(key)
else:
self._insert(node.right, key)
def search(self, key):
if self.root is None:
return False
else:
return self._search(self.root, key)
def _search(self, node, key):
if node is None:
return False
elif node.key == key:
return True
elif key < node.key:
return self._search(node.left, key)
else:
return self._search(node.right, key)
def preorder(self):
if self.root is None:
return
else:
self._preorder(self.root)
print()
def _preorder(self, node):
if node is None:
return
print(node.key, end=" ")
self._preorder(node.left)
self._preorder(node.right)
def breadthfirst(self):
if self.root is None:
return
else:
self._breadthfirst(self.root)
print()
def _breadthfirst(self, node):
if node is None:
return
queue = []
queue.append(node)
while len(queue) > 0:
print(queue[0].key, end=" ")
node = queue.pop(0)
if node.left is not None:
queue.append(node.left)
if node.right is not None:
queue.append(node.right)
def remove(self, key):
if self.root is None:
return
else:
self._remove(self.root, key)
def _remove(self, node, key):
if node is None:
return node
if key < node.key:
node.left = self._remove(node.left, key)
elif key > node.key:
node.right = self._remove(node.right, key)
else:
if node.left is None:
temp = node.right
node = None
return temp
elif node.right is None:
temp = node.left
node = None
return temp
temp = self._minValueNode(node.right)
node.key = temp.key
node.right = self._remove(node.right, temp.key)
return node
def _minValueNode(self, node):
if node is None:
return node
while node.left is not None:
node = node.left
return node
if __name__ == "__main__":
Tree = BST()
keys = [5, 9, 1, 3, 7, 4, 6, 2]
for key in keys:
Tree.insert(key)
Tree.preorder() # 5 1 3 2 4 9 7 6
Tree.breadthfirst() # 5 1 9 3 7 2 4 6

View File

@@ -0,0 +1,128 @@
class AVLNode:
# Constructor for new node
def __init__(self, key: int):
self.key = key
self.left = None
self.right = None
self.balance = 0
class AVL:
# Constructor for new AVL
def __init__(self):
self.root = None
self.is_balanced = True
# Inserts a new key to the search tree
def insert(self, key):
self.root = self.insert_help(self.root, key)
# Help function for insert
def insert_help(self, root, key):
if not root:
root = AVLNode(key)
self.is_balanced = False
elif key < root.key:
root.left = self.insert_help(root.left, key)
if not self.is_balanced: # Check for possible rotations
if root.balance >= 0: # No Rotations needed, update balance variables
self.is_balanced = root.balance == 1
root.balance -= 1
# Rotation(s) needed
else:
if root.left.balance == -1:
root = self.right_rotation(root) # Single rotation
else:
root = self.left_right_rotation(
root) # Double rotation
self.is_balanced = True
elif key > root.key:
root.right = self.insert_help(root.right, key)
if not self.is_balanced: # Check for possible rotations
if root.balance <= 0: # No Rotations needed, update balance variables
self.is_balanced = root.balance == -1
root.balance += 1
# Rotation(s) needed
else:
if root.right.balance == 1:
root = self.left_rotation(root)
else:
root = self.right_left_rotation(root)
self.is_balanced = True
return root
# Single rotation: right rotation around root
def right_rotation(self, root):
child = root.left # Set variable for child node
root.left = child.right # Rotate
child.right = root
child.balance = root.balance = 0 # Fix balance variables
return child
# Single rotation: left rotation around root
def left_rotation(self, root):
child = root.right # Set variable for child node
root.right = child.left # Rotate
child.left = root
child.balance = root.balance = 0 # Fix balance variables
return child
# Double rotation: left rotation around child node followed by right rotation around root
def left_right_rotation(self, root):
child = root.left
# Set variables for child node and grandchild node
grandchild = child.right
child.right = grandchild.left # Rotate
grandchild.left = child
root.left = grandchild.right
grandchild.right = root
root.balance = child.balance = 0 # Fix balance variables
if grandchild.balance == -1:
root.balance = 1
elif grandchild.balance == 1:
child.balance = -1
grandchild.balance = 0
return grandchild
# Double rotation: right rotation around child node followed by left rotation around root
def right_left_rotation(self, root):
child = root.right
# Set variables for child node and grandchild node
grandchild = child.left
child.left = grandchild.right # Rotate
grandchild.right = child
root.right = grandchild.left
grandchild.left = root
root.balance = child.balance = 0 # Fix balance variables
if grandchild.balance == 1:
root.balance = -1
elif grandchild.balance == -1:
child.balance = 1
grandchild.balance = 0
return grandchild
def preorder(self):
print(self.preorder_help(self.root))
def preorder_help(self, root):
if not root:
return ''
return str(root.key) + ';' + str(root.balance) + ' ' + self.preorder_help(root.left) + self.preorder_help(root.right)
# Returns the height of the tree
def height(self):
return self.height_help(self.root)
def height_help(self, root):
if not root:
return 0
return 1 + max(self.height_help(root.left), self.height_help(root.right))
if __name__ == "__main__":
# Make a list from 5 19 3 9 4 16 13 8 14 11 7 17 2 15 1 10 6 12 20 18
Tree = AVL()
for key in [5, 19, 3, 9, 4, 16, 13, 8, 14, 11, 7, 17, 2, 15, 1, 10, 6, 12, 20, 18]:
Tree.insert(key)
Tree.preorder()

View File

@@ -0,0 +1,172 @@
<!doctype html>
<html>
<head>
<title>Week 6 Programming Assignments (8 points)</title>
<meta charset="utf-8">
</head>
<body>
<div>
<h4><strong>Assignment 6.1: The AVL Tree</strong><span>&nbsp;(5 points)</span></h4>
</div>
<div>The following Python code implements the AVL tree which can do single and double rotations to the right. Finalize the class <strong>AVL</strong> by creating following methods:</div>
<ul>
<ul>
<li><strong>left_rotation(root: AVLNode)</strong>: symmetrical to
<strong>right_rotation</strong>
</li>
<li><strong>right_left_rotation(root: AVLNode)</strong>: symmetrical to <strong>left_right_rotation</strong></li>
<li><strong>preorder()</strong>: enumerates the keys and their balance values in preorder</li>
<ul>
<li>the format: 'key1:balance1 key2:balance2 key3:balance3'</li>
</ul>
</ul>
</ul>
<div>Finally, finalize the method <strong>insert_help</strong> so that it functions properly.</div>
<p></p>
<div><span>
<div>Submit your solution in CodeGrade as <strong>AVL.py</strong>.</div>
</span><br></div>
<div>An example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>if __name__ == "__main__":
Tree = AVL()
for key in [9, 10, 11, 3, 2, 6, 4, 7, 5, 1]:
Tree.insert(key)
Tree.preorder() # 9;-1 4;0 2;0 1;0 3;0 6;0 5;0 7;0 10;1 11;0
</pre>
</div>
<div><br></div>
<div>A code template for the AVL tree: </div>
<p></p>
<div style="border:2px solid black">
<pre>class AVLNode:
# Constructor for new node
def __init__(self, key: int):
self.key = key
self.left = None
self.right = None
self.balance = 0
class AVL:
# Constructor for new AVL
def __init__(self):
self.root = None
self.is_balanced = True
# Inserts a new key to the search tree
def insert(self, key):
self.root = self.insert_help(self.root, key)
# Help function for insert
def insert_help(self, root, key):
if not root:
root = AVLNode(key)
self.is_balanced = False
elif key &lt; root.key:
root.left = self.insert_help(root.left, key)
if not self.is_balanced: # Check for possible rotations
if root.balance &gt;= 0: # No Rotations needed, update balance variables
self.is_balanced = root.balance == 1
root.balance -= 1
else: # Rotation(s) needed
if root.left.balance == -1:
root = self.right_rotation(root) # Single rotation
else:
root = self.left_right_rotation(root) # Double rotation
self.is_balanced = True
elif key &gt; root.key:
root.right = self.insert_help(root.right, key)
# TODO
return root
# Single rotation: right rotation around root
def right_rotation(self, root):
child = root.left # Set variable for child node
root.left = child.right # Rotate
child.right = root
child.balance = root.balance = 0 # Fix balance variables
return child
# Single rotation: left rotation around root
def left_rotation(self, root):
# TODO
# Double rotation: left rotation around child node followed by right rotation around root
def left_right_rotation(self, root):
child = root.left
grandchild = child.right # Set variables for child node and grandchild node
child.right = grandchild.left # Rotate
grandchild.left = child
root.left = grandchild.right
grandchild.right = root
root.balance = child.balance = 0 # Fix balance variables
if grandchild.balance == -1:
root.balance = 1
elif grandchild.balance == 1:
child.balance = -1
grandchild.balance = 0
return grandchild
# Double rotation: right rotation around child node followed by left rotation around root
def right_left_rotation(self, root):
# TODO
</pre>
</div>
<br>
<br>
<div>
<h4><strong>Assignment 6.2: The Min Heap</strong>&nbsp;(3 points)</h4>
</div>
<div>Implement the min heap structure that stores positive integers in Python. Create class <strong>MinHeap</strong> which has following functions:</div>
<ul>
<ul>
<li><strong>push(key: int)</strong>: inserts a new key to the heap while maintaining the min heap property.<br></li>
<li><strong>pop(): </strong>removes the smallest key from the heap and returns its value. </li>
<li><strong>print()</strong>: prints the heap in breadth-first order<ul>
<li>the format: key values separated by spaces (e.g. 1 2 3 4).<br></li>
</ul>
</li>
</ul>
</ul>
<div>Make sure that the heap always maintains the min heap property. The best practice is to store the heap structure in a list where a key in index \(i\)</div>
<ul>
<ul>
<li>has a left child at index \(2i + 1\)</li>
<li>has a right child at index \(2i + 2\)</li>
<li>has its parent at index \(\lfloor \frac{i-1}{2} \rfloor\) (\(\lfloor \ldots \rfloor\) rounds down to the nearest integer)<br></li>
</ul>
</ul>
<div>A code template an example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>class MinHeap:
# TODO
if __name__ == "__main__":
items = [4, 8, 6, 5, 1, 2, 3]
heap = MinHeap()
[heap.push(key) for key in items]
heap.print() # 1 4 2 8 5 6 3
print(heap.pop()) # 1
heap.print() # 2 4 3 8 5 6
</pre>
</div>
<p></p>
<div>Submit your solution in CodeGrade as <strong>minheap.py</strong>.</div>
</body>
</html>

View File

@@ -0,0 +1,49 @@
class MinHeap:
def __init__(self):
self.heap = []
def push(self, key):
self.heap.append(key)
self.bubble_up(len(self.heap) - 1)
def bubble_up(self, index):
if index == 0:
return
parent = (index - 1) // 2
if self.heap[parent] > self.heap[index]:
self.heap[parent], self.heap[index] = self.heap[index], self.heap[parent]
self.bubble_up(parent)
def pop(self):
if len(self.heap) == 0:
return None
if len(self.heap) == 1:
return self.heap.pop()
self.heap[0], self.heap[-1] = self.heap[-1], self.heap[0]
min = self.heap.pop()
self.bubble_down(0)
return min
def bubble_down(self, index):
left = 2 * index + 1
right = 2 * index + 2
smallest = index
if left < len(self.heap) and self.heap[left] < self.heap[smallest]:
smallest = left
if right < len(self.heap) and self.heap[right] < self.heap[smallest]:
smallest = right
if smallest != index:
self.heap[smallest], self.heap[index] = self.heap[index], self.heap[smallest]
self.bubble_down(smallest)
def print(self):
print(' '.join(map(str, self.heap)))
if __name__ == "__main__":
items = [4, 8, 6, 5, 1, 2, 3]
heap = MinHeap()
[heap.push(key) for key in items]
heap.print() # 1 4 2 8 5 6 3
print(heap.pop()) # 1
heap.print() # 2 4 3 8 5 6

View File

@@ -0,0 +1,121 @@
<!doctype html>
<html>
<head>
<title>Week 7 Programming Assignments (9 points)</title>
<meta charset="utf-8">
</head>
<body>
<div>
<h4><span>Assignment 7.1: The Quicksort</span> <span style="font-weight: normal;">(4 point)</span><br></h4>
</div>
<div>Familiarize yourself with the principle of Quicksort from the background material and implement Quicksort in Python.
Create a function <strong>qsort(A: list, i: int, j: int)</strong> which has the following input parameters:</div>
<ul>
<ul>
<li>\(A\): a (whole) list of integers</li>
<li>\(i\): a start index of selected partition</li>
<li>\(j\): a end index of selected partition</li>
</ul>
</ul>
<div>The function has no return value since the sorting process is done to the original list. You can select the pivot index/value as you wish.</div>
<br>
<div>Limits:</div>
<ul>
<ul>
<li>maximum list size is \(10^4\)</li>
<li>all elements of the list are integers between \(1 \dots 10^4\)</li>
</ul>
</ul>
<div>A code template with an example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>def qsort(A, i, j):
# TODO
if __name__ == "__main__":
A = [9, 7, 1, 8, 5, 3, 6, 2, 4]
print(A) # [9, 7, 1, 8, 5, 3, 6, 2, 4]
qsort(A, 0, len(A)-1)
print(A) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
</pre>
</div>
<br>
<div>Submit your solution in CodeGrade as <strong>quicksort.py</strong>.</div>
<br>
<br>
<div>
<h4>Assignment 7.2: Car Sales<span style="font-weight: normal;"> (3 points)</span><br></h4>
</div>
<div>A car shop has cars \(A = [a_1, a_2, ..., a_n]\) (one of each) where \(a_i\) is the price of the car \(i\). Customers \(B = [b_1, b_2, ..., b_m]\) arrive to the shop.
\(b_i\) is the price that the customer \(i\) can afford. What is the maximum amount of sales that can be made?<br></div>
<br>
<div>For example the shop has cars \(A = [20, 10, 15, 26]\) and there are customers \(B = [11, 25, 15, 9]\) it is possible to make \(3\) sales.</div>
<ul>
<ul>
<li>The first customer (\(11\)) gets car that cost \(10\), The second customer (\(25\)) gets the car that cost \(20\), and the third customer (\(15\)) gets the car that cost \(15\).</li>
</ul>
</ul>
<div>Create a function <strong>sales(A: list, B: list)</strong> in Python which returns the number of possible sales.</div>
<br>
<div>Limits:</div>
<ul>
<ul>
<li>\(1 \leq n,m \leq 10^4\)</li>
<li>\(1 \leq a_i, b_i \leq 10^4\)</li>
</ul>
</ul>
<div>Target: the function performs in \(\Theta(n \log n)\) time.</div>
<br>
<div>A code template with an example program: </div>
<p></p>
<div style="border:2px solid black">
<pre>def sales(cars, customers) -&gt; int:
# TODO
if __name__ == "__main__":
print(sales([20, 10, 15], [11, 25, 15])) # 3
print(sales([13, 7, 2, 3, 12, 4, 19], [3, 25, 16, 14])) # 4
print(sales([24, 6, 20, 21, 12, 5], [25, 1, 24, 15])) # 3
print(sales([14, 9, 10, 15, 18, 20], [24, 17, 9, 22, 12, 4])) # 5
</pre>
</div>
<br>
<div>Submit your solution in CodeGrade as <strong>sales.py</strong>.</div>
<br>
<br>
<div>
<h4>Assignment 7.3: Subsets<span style="font-weight: normal;"> (2 points)</span><br></h4>
</div>
<div>A given set that has numbers from \(1\) to \(N\) in increasing order (\(\{1, 2, 3, 4, \dots, N\}\)), create a function <strong>subsets(N: int)</strong> in Python which produces a list of all possible subsets.</div>
<br>
<div>For example when \(N = 3\) the subsets are \([1]\), \([2]\), \([1, 2]\), \([3]\), \([1, 3]\), \([2, 3]\) and \([1, 2, 3]\).</div>
<br>
<div>The function must return the list of subsets in specific order (see the example program below).<br></div><p></p><div>Limits: \(1 \leq N \leq 20\)<br></div><div><br></div><div>A code template with an example program:
</div><p></p>
<div style="border:2px solid black">
<pre>def subsets(n: int) -&gt; list:
# TODO
if __name__ == "__main__":
print(subsets(3)) # [[1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
print(subsets(4)) # [[1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3],
# [4], [1, 4], [2, 4], [1, 2, 4], [3, 4], [1, 3, 4],
# [2, 3, 4], [1, 2, 3, 4]]
S = subsets(10)
print(S[95]) # [6, 7]
print(S[254]) # [1, 2, 3, 4, 5, 6, 7, 8]
print(S[826]) # [1, 2, 4, 5, 6, 9, 10]
</pre>
</div>
<p><br></p><p>Submit your solution in CodeGrade as <strong>subsets.py</strong></p>
</body>
</html>

View File

@@ -0,0 +1,24 @@
def qsort(A, i, j):
if i >= j:
return
p = partition(A, i, j)
qsort(A, i, p-1)
qsort(A, p+1, j)
def partition(A, i, j):
pivot = A[j]
p = i
for q in range(i, j):
if A[q] < pivot:
A[p], A[q] = A[q], A[p]
p += 1
A[p], A[j] = A[j], A[p]
return p
if __name__ == "__main__":
A = [9, 7, 1, 8, 5, 3, 6, 2, 4]
print(A) # [9, 7, 1, 8, 5, 3, 6, 2, 4]
qsort(A, 0, len(A)-1)
print(A) # [1, 2, 3, 4, 5, 6, 7, 8, 9]

View File

@@ -0,0 +1,18 @@
def sales(cars, customers) -> int:
cars.sort()
customers.sort()
sales = 0
for customer in customers:
for car in cars:
if car <= customer:
sales += 1
cars.remove(car)
break
return sales
if __name__ == "__main__":
print(sales([20, 10, 15], [11, 25, 15])) # 3
print(sales([13, 7, 2, 3, 12, 4, 19], [3, 25, 16, 14])) # 4
print(sales([24, 6, 20, 21, 12, 5], [25, 1, 24, 15])) # 3
print(sales([14, 9, 10, 15, 18, 20], [24, 17, 9, 22, 12, 4])) # 5

View File

@@ -0,0 +1,26 @@
def subsets(a: int) -> list:
s = [i for i in range(1, a+1)]
return [x for x in get_sets(s) if x]
def get_sets(s):
size = 2**len(s)
for cnt in range(0, size):
final = []
for i in range(0, len(s)):
if ((cnt & (1 << i)) > 0):
final.append(s[i])
yield final
if __name__ == "__main__":
print(subsets(3)) # [[1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
print(subsets(4)) # [[1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3],
# [4], [1, 4], [2, 4], [1, 2, 4], [3, 4], [1, 3, 4],
# [2, 3, 4], [1, 2, 3, 4]]
S = subsets(10)
print(S[95]) # [6, 7]
print(S[254]) # [1, 2, 3, 4, 5, 6, 7, 8]
print(S[826]) # [1, 2, 4, 5, 6, 9, 10]

View File

@@ -0,0 +1,103 @@
<!doctype html>
<html>
<head>
<title>Week 8 Programming Assignments (8 points)</title>
<meta charset="utf-8">
</head>
<body>
<h4><strong>Assignment 8.1: Jumps</strong> (4 points)</h4>
<div>You are playing a game which has \(n\) levels. You start from the level \(0\) and your goal is to reach the level
\(n\) by jumping from a level to another. From every level you're able to jump only to \(a\) or \(b\)
levels higher. In how many different ways can you complete the game?
</div>
<br>
<div>e.g.: let \(n=8\), \(a=2\) and \(b=3\), there are \(4\) different ways to pass the game.</div>
<ul>
<ul>
<li>\(0\rightarrow 2\rightarrow 4\rightarrow 6\rightarrow 8\)</li>
<li>\(0\rightarrow 2\rightarrow 5\rightarrow 8\)</li>
<li>\(0\rightarrow 3\rightarrow 5\rightarrow 8\)</li>
<li>\(0\rightarrow 3\rightarrow 6\rightarrow 8\)</li>
</ul>
</ul>
<div>Create a function <strong>jumps(n: int, a: int, b: int)</strong> in Python which returns the number of all possible ways to complete the game.</div>
<br>
<div>Limist (you can assume that):</div>
<ul>
<ul>
<li>\(n \leq 10000\)</li>
<li>\(1 \leq a &lt; b \leq n\)</li>
</ul>
</ul>
<div>Targets:</div>
<ol>
<li>correct solution: \(2\) points</li>
<li>performs in \(\Theta (n)\) time: \(+2\) points (the solution must be correct)</li>
</ol>
<div>A code template with an example program:</div>
<br>
<div style="border: 2px solid black">
<pre>def jumps(n, a, b):
# TODO
if __name__ == "__main__":
print(jumps(4, 1, 2)) # 5
print(jumps(8, 2, 3)) # 4
print(jumps(11, 6, 7)) # 0
print(jumps(30, 3, 5)) # 58
print(jumps(100, 4, 5)) # 1167937
</pre>
</div>
<br>
<div>Submit your solution in CodeGrade as <strong>jumps.py</strong>.</div>
<br><br>
<br>
<div>
<h4><strong>Assignment 8.2: All Sums </strong>(4 points)</h4>
</div>
<div>\(A\) is a list consisting of \(n\) integers. How many different sums can be generated with the given integers?</div>
<br>
<div>For example:</div>
<ul>
<ul>
<li>list \([1, 2, 3]\) has 6 possible sums: \(1\), \(2\), \(3\), \(4\), \(5\) and \(6\)</li>
<li>list \([2, 2, 3]\) has 5 possible sums: \(2\), \(3\), \(4\), \(5\) and \(7\)</li>
</ul>
</ul>
<div>Create a function <strong>sums(A: list)</strong> in Python which computes the number of all different sums.</div>
<br>
<div>Limits (you can assume that): \(1 \leq n, a_i \leq 100\)</div>
<br>
<div>Target: The algorithm computes the awnser in less than \(1\) second (in CodeGrade)</div>
<ol>
<li>correct solution: \(2\) points</li>
<li>performs in \(\Theta (n^3)\) time: \(+2\) points&nbsp;(the solution be must correct)</li>
<ul>
<li>(extra: consider the running time of your algorithm)</li>
</ul>
</ol>
<div>A code template with an example program:</div>
<br>
<div style="border: 2px solid black">
<pre>def sums(items):
# TODO
if __name__ == "__main__":
print(sums([1, 2, 3])) # 6
print(sums([2, 2, 3])) # 5
print(sums([1, 3, 5, 1, 3, 5])) # 18
print(sums([1, 15, 5, 23, 100, 55, 2])) # 121
</pre>
</div>
<br>
<div>Submit your solution in CodeGrade as <strong>sums.py</strong>.</div>
</body>
</html>

View File

@@ -0,0 +1,24 @@
def jumps(n, a, b):
if n < 0:
return 0
if n == 0:
return 1
# Initialize an array to store the number of ways to reach each position
num_ways = [0] * (n + 1)
num_ways[0] = 1
# Calculate the number of ways to reach each position
for i in range(n + 1):
num_ways[i] += num_ways[i - a]
num_ways[i] += num_ways[i - b]
return num_ways[n]
if __name__ == "__main__":
print(jumps(4, 1, 2)) # 5
print(jumps(8, 2, 3)) # 4
print(jumps(11, 6, 7)) # 0
print(jumps(30, 3, 5)) # 58
print(jumps(100, 4, 5)) # 1167937

View File

@@ -0,0 +1,15 @@
def sums(arr):
sums = set()
for i in range(len(arr)):
for j in range(i, len(arr)):
for k in range(j, len(arr)):
sums.add(arr[i] + arr[j] + arr[k])
return len(sums)
# Test
if __name__ == "__main__":
print(sums([1, 2, 3])) # 6
print(sums([2, 2, 3])) # 5
print(sums([1, 3, 5, 1, 3, 5])) # 18
print(sums([1, 15, 5, 23, 100, 55, 2])) # 121

View File

@@ -0,0 +1,32 @@
def sums(arr):
# Create a set to store all the unique sums
sums = set()
# Initialize a list with length equal to the length of arr
# to store the previous results
prev_results = [0] * len(arr)
# Iterate through each element in arr
for i in range(len(arr)):
# Initialize a temporary set to store the new sums
temp = set()
# Iterate through all the previous results
for j in range(i):
# Add the current element to each of the previous sums
# and add the new sum to the set
temp.add(arr[i] + prev_results[j])
# Add the current element to the set
temp.add(arr[i])
# Update the list of previous results with the new sums
prev_results[i] = temp
# Update the set of all sums with the new sums
sums = sums.union(temp)
return len(sums)
# Test
if __name__ == "__main__":
print(sums([1, 2, 3])) # 6
print(sums([2, 2, 3])) # 5
print(sums([1, 3, 5, 1, 3, 5])) # 18
print(sums([1, 15, 5, 23, 100, 55, 2])) # 121

View File

@@ -0,0 +1,118 @@
<!doctype html>
<html>
<head>
<title>Week 9 Programming Assignments (8 points)</title>
<meta charset="utf-8">
</head>
<body>
<div>
<h4><span>Assignment 9.1: Creating a Graph</span> <span style="font-weight: normal;">(4 points)</span><br></h4>
</div>
<div><span class="" style="color: rgb(239, 69, 64);"></span>Your task is to create a class <strong>Graph</strong> in python which stores a weighted directed or undirected graph structure. The initializer method takes a \(V \times V\) weighted adjacency matrix as an input value (\(V\) sized list of \(V\) sized lists).
The class must have following methods:</div>
<ul>
<ul>
<li><strong>df_print(start: int)</strong>: prints out the graph in depth-first order from the start vertex (vertex numbers separated by space).</li>
<li><strong>bf_print(start: int)</strong>: prints out the graph in breadth-first order from the start vertex.</li>
<li><strong>weight(vertex1: int, vertex2: int)</strong>: returns the weight from vertex1 to vertex2, returns -1 if there is no edge between the vertices.</li>
</ul>
</ul>
<div>where start vertex is labeled between \(0\ ...\ V-1\).</div>
<p></p>
<div>Feel free to store the graph structure to your class as you wish as long as the given functions above work as intended. You can use the adjacency matrix
or create a adjacency list to keep track of neighbors of each vertex (or even both).</div>
<div><br>
</div>
<div><strong><span class="" style="background-color: rgb(255, 255, 255); color: rgb(239, 69, 64);">Note: </span></strong><span class="" style="background-color: rgb(255, 255, 255); color: rgb(239, 69, 64);">This graph class will be used in following assignments related to graphs so it is recommended to complete this assignment before moving to others.</span></div>
<p></p>
<div><span>A code template with an example program </span>for the directed graph below:</div>
<p></p>
<!-- Add other path in Moodle! -->&nbsp;<img src="data/examplegraph.png?time=1657884314459" alt="Example Graph" class="img-responsive atto_image_button_text-bottom" width="306" height="239">
<p></p>
<div style="border:2px solid black">
<pre>class Graph:
# TODO
if __name__ == "__main__":
matrix = [
# 0 1 2 3 4 5
[0, 0, 7, 0, 9, 0], # 0
[0, 0, 0, 0, 0, 0], # 1
[0, 5, 0, 1, 0, 2], # 2
[6, 0, 0, 0, 0, 2], # 3
[0, 0, 0, 0, 0, 1], # 4
[0, 6, 0, 0, 0, 0] # 5
]
graph = Graph(matrix)
graph.df_print(0) # 0 2 1 3 5 4
graph.bf_print(0) # 0 2 4 1 3 5
print(graph.weight(0, 2)) # 7
print(graph.weight(3, 4)) # -1
</pre>
</div>
<p></p>
<div>Submit your solution in CodeGrade as <strong>graph.py</strong>.</div>
<br>
<br>
<div>
<h4><span><span>Assignment 9.2: Dijkstra's Algoritmh</span></span> <span style="font-weight: normal;">(4 points)</span><br></h4>
</div>
<div>With Dijkstra's algorithm we not only get the shortest paths from the start vertex to other vertices but we can build a new
graph with only the arcs that constructs those paths.</div>
<br>
<div>Create a function <strong>dijkstragraph(graph: Graph, start: int)</strong> that takes a Graph object and the start vertex as an input value. The function
returns a new Graph object that has only the arcs that constructs the shortest paths computed by Dijkstra's algorithm. The new graph will be directed despite
whether the original graph was directed or undirected.</div>
<br>
<div>For example the on the left we have the original directed graph and on the right we have a graph that the function produces, starting from vertex \(0\).</div>
<br>
<p>&nbsp;<img src="data/dijkstra.png?time=1657884289742" alt="Example Graph" class="img-responsive atto_image_button_text-bottom" width="776" height="240">
</p><br>
<div><span>A code template with an example program </span>for the graph:</div>
<br>
<div style="border:2px solid black">
<pre>from graph import Graph
def dijkstragraph(graph, start):
# TODO
if __name__ == "__main__":
matrix = [
[0, 25, 6, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 10, 3, 0, 0, 0, 0, 0],
[0, 0, 0, 7, 0, 25, 0, 0, 0, 0],
[0, 0, 0, 0, 12, 15, 4, 15, 20, 0],
[0, 0, 0, 0, 0, 0, 0, 2, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 2, 0],
[0, 0, 0, 0, 0, 0, 0, 8, 13, 15],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 5],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
graph = Graph(matrix)
new_graph = dijkstragraph(graph, 0)
new_graph.df_print(0) # 0 1 2 3 4 5 6 7 9 8
new_graph.bf_print(0) # 0 1 2 3 4 5 6 7 8 9
print(new_graph.weight(3, 6)) # 4
print(new_graph.weight(5, 8)) # -1
</pre>
</div>
<br>
<div>Submit your solution in CodeGrade as <strong>dijkstra.py</strong> including your graph class in <strong>graph.py</strong> (Assignment 9.1).</div>
<br>
<div>Hint: try to do the Dijkstra's algorithm and new graph construction separately. What other information can we get while running Dijkstra's algorithm?</div>
</body>
</html>

View File

@@ -0,0 +1,53 @@
class Graph:
def __init__(self, adj_matrix):
self.adj_matrix = adj_matrix
self.V = len(adj_matrix)
def df_print(self, start):
visited = [False] * self.V
self.dfs(start, visited)
print()
def dfs(self, start, visited):
visited[start] = True
print(start, end=' ')
for i in range(self.V):
if self.adj_matrix[start][i] > 0 and not visited[i]:
self.dfs(i, visited)
def bf_print(self, start):
visited = [False] * self.V
queue = []
queue.append(start)
visited[start] = True
while queue:
start = queue.pop(0)
print(start, end=' ')
for i in range(self.V):
if self.adj_matrix[start][i] > 0 and not visited[i]:
queue.append(i)
visited[i] = True
print()
def weight(self, vertex1, vertex2):
return self.adj_matrix[vertex1][vertex2] if self.adj_matrix[vertex1][vertex2] > 0 else -1
if __name__ == "__main__":
matrix = [
# 0 1 2 3 4 5
[0, 0, 7, 0, 9, 0], # 0
[0, 0, 0, 0, 0, 0], # 1
[0, 5, 0, 1, 0, 2], # 2
[6, 0, 0, 0, 0, 2], # 3
[0, 0, 0, 0, 0, 1], # 4
[0, 6, 0, 0, 0, 0] # 5
]
graph = Graph(matrix)
graph.df_print(0) # 0 2 1 3 5 4
graph.bf_print(0) # 0 2 4 1 3 5
print(graph.weight(0, 2)) # 7
print(graph.weight(3, 4)) # -1