Skip to content

Commit

Permalink
Refactored solutions 306 - 319
Browse files Browse the repository at this point in the history
  • Loading branch information
WHAHA-HA committed Feb 1, 2021
1 parent 86df7a5 commit 6f0eae5
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 67 deletions.
14 changes: 7 additions & 7 deletions Solutions/306.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@
Come up with an algorithm that sorts this list in O(N log k) time.
"""

from heapq import heappop, heappush, heapify
from typing import List

from DataStructures.Heap import MinHeap


def k_sort(arr: List[int], k: int) -> List[int]:
length = len(arr)
# generating the heap
heap = arr[: k + 1]
heapify(heap)
heap = MinHeap()
[heap.insert(elem) for elem in arr[: k + 1]]
# updating the values of the array (to hold sorted elements)
curr_index = 0
for index in range(k + 1, length):
arr[curr_index] = heappop(heap)
heappush(heap, arr[index])
arr[curr_index] = heap.extract_min()
heap.insert(arr[index])
curr_index += 1
# updating the last k positions in the array by emptying the heap
while heap:
arr[curr_index] = heappop(heap)
arr[curr_index] = heap.extract_min()
curr_index += 1
return arr

Expand Down
22 changes: 12 additions & 10 deletions Solutions/307.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,14 @@ def get_ceiling(node: Node, value: int) -> Optional[int]:
if node.left:
if node.left.val >= value:
return get_ceiling(node.left, value)
else:
return node.val
else:
return node.val
return node.val
elif node.val == value:
return value
else:
if node.right:
return get_ceiling(node.right, value)
else:
return None
return None


def get_floor(node: Node, value: int) -> Optional[int]:
Expand All @@ -40,17 +37,14 @@ def get_floor(node: Node, value: int) -> Optional[int]:
if node.right:
if node.right.val <= value:
return get_floor(node.right, value)
else:
return node.val
else:
return node.val
return node.val
elif node.val == value:
return value
else:
if node.left:
return get_floor(node.left, value)
else:
return None
return None


def get_floor_and_ceiling(
Expand All @@ -75,3 +69,11 @@ def get_floor_and_ceiling(
print(get_floor_and_ceiling(tree, 7))
print(get_floor_and_ceiling(tree, -1))
print(get_floor_and_ceiling(tree, 5))


"""
SPECS:
TIME COMPLEXITY: O(log(n))
SPACE COMPLEXITY: O(log(n))
"""
2 changes: 0 additions & 2 deletions Solutions/310.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@


def get_set_bits(num: int) -> int:
# get the number of bits set in a number [runs in O(log(n))]
bin_num = bin(num)[2:]
return sum([int(digit) for digit in bin_num])


def get_total_set_bits(N: int) -> int:
# sums up the number of bits set in all positive numbers till N
result = 0
for i in range(1, N + 1):
result += get_set_bits(i)
Expand Down
4 changes: 2 additions & 2 deletions Solutions/313.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def get_min_moves_helper(
# if a loop back occours, the target pattern cannot be reached
return maxsize
seen.add(curr_val)
# generating moves

moves = []
for i in range(3):
temp = curr.copy()
Expand All @@ -53,7 +53,7 @@ def get_min_moves_helper(
temp[i] = turn_wheel_down(temp[i])
if tuple(temp) not in dead_ends:
moves.append(temp)
# getting the minimum number of moves required to reach the pattern

temp = maxsize
for move in moves:
temp = min(
Expand Down
1 change: 0 additions & 1 deletion Solutions/314.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ def get_min_range(listeners: List[int], towers: List[int]) -> int:
listeners_distance = {listener: maxsize for listener in listeners}
for listener in listeners:
for tower in towers:
# updating distance
listeners_distance[listener] = min(
listeners_distance[listener], abs(tower - listener)
)
Expand Down
3 changes: 1 addition & 2 deletions Solutions/316.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@


def count_ways_to_generate_change(changes: List[int], target: int) -> int:
# function to generate in how many ways the given change can be reached
length = len(changes)
if not length:
return 0
Expand All @@ -37,7 +36,7 @@ def count_ways_to_generate_change(changes: List[int], target: int) -> int:
def get_changes(num_ways_to_get_change: List[int]) -> List[int]:
length = len(num_ways_to_get_change)
changes_list = []
# generating the changes list

for i in range(1, length):
if num_ways_to_get_change[i] > 0:
count = count_ways_to_generate_change(changes_list, i)
Expand Down
77 changes: 34 additions & 43 deletions Solutions/319.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,27 @@
Design a class to represent the board, and find a series of steps to bring the board
to the state [[1, 2, 3], [4, 5, 6], [7, 8, None]].
"""
# this is an improvised version of the method available at:
# https://gist.github.com/flatline/838202

# SOLUTION FROM: https://gist.github.com/flatline/838202 (MODIFIED AND OPTIMIZED)

from __future__ import annotations
from math import sqrt
from typing import Callable, List, Mapping, Tuple, Union

FINAL_STATE = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 0]
]
FINAL_STATE = [[1, 2, 3], [4, 5, 6], [7, 8, 0]]


def index(item, seq):
def index(item: EightPuzzle, seq: List[EightPuzzle]) -> int:
"""
Helper function that returns -1 for non-found index value of a seq
"""
if item in seq:
return seq.index(item)
else:
return -1
return -1


class EightPuzzle:
def __init__(self, board):
def __init__(self, board: List[List[int]]) -> None:
# heuristic value
self._hval = 0
# search depth of current instance
Expand All @@ -41,25 +38,22 @@ def __init__(self, board):
self.adj_matrix = []
self.adj_matrix = board

def __eq__(self, other):
if self.__class__ != other.__class__:
return False
else:
return self.adj_matrix == other.adj_matrix
def __eq__(self, other: EightPuzzle) -> bool:
return self.adj_matrix == other.adj_matrix

def __str__(self):
def __str__(self) -> str:
res = ""
for row in range(3):
res += " ".join(map(str, self.adj_matrix[row]))
res += "\r\n"
return res

def _clone(self):
def _clone(self) -> EightPuzzle:
copy = [[elem for elem in row] for row in self.adj_matrix]
p = EightPuzzle(copy)
return p

def _get_legal_moves(self):
def _get_legal_moves(self) -> List[Tuple[int, int]]:
"""
Returns list of tuples with which the free space may be swapped
"""
Expand All @@ -78,11 +72,11 @@ def _get_legal_moves(self):

return free

def _generate_moves(self):
def _generate_moves(self) -> Mapping[EightPuzzle]:
free = self._get_legal_moves()
zero = self.find(0)

def swap_and_clone(a, b):
def swap_and_clone(a: int, b: int) -> EightPuzzle:
p = self._clone()
p.swap(a, b)
p._depth = self._depth + 1
Expand All @@ -91,20 +85,19 @@ def swap_and_clone(a, b):

return map(lambda pair: swap_and_clone(zero, pair), free)

def _generate_solution_path(self, path):
if self._parent == None:
def _generate_solution_path(self, path: List[EightPuzzle]):
if self._parent is None:
return path
else:
path.append(self)
return self._parent._generate_solution_path(path)
path.append(self)
return self._parent._generate_solution_path(path)

def solve(self, h):
def solve(self, h: Callable) -> Tuple[List[EightPuzzle], int]:
"""
Performs A* search for goal state.
h(puzzle) - heuristic function, returns an integer
"""

def is_solved(puzzle):
def is_solved(puzzle: EightPuzzle) -> bool:
return puzzle.adj_matrix == FINAL_STATE

openl = [self]
Expand Down Expand Up @@ -150,7 +143,7 @@ def is_solved(puzzle):
# if finished state not found, return failure
return [], 0

def find(self, value):
def find(self, value: int) -> Tuple[int, int]:
"""
returns the row, col coordinates of the specified value in the graph
"""
Expand All @@ -162,19 +155,19 @@ def find(self, value):
if self.adj_matrix[row][col] == value:
return row, col

def peek(self, row, col):
def peek(self, row: int, col: int) -> int:
"""
returns the value at the specified row and column
"""
return self.adj_matrix[row][col]

def poke(self, row, col, value):
def poke(self, row: int, col: int, value: int) -> int:
"""
sets the value at the specified row and column
"""
self.adj_matrix[row][col] = value

def swap(self, pos_a, pos_b):
def swap(self, pos_a: Tuple[int, int], pos_b: Tuple[int, int]) -> None:
"""
swaps values at the specified coordinates
"""
Expand All @@ -183,7 +176,9 @@ def swap(self, pos_a, pos_b):
self.poke(pos_b[0], pos_b[1], temp)


def heur(puzzle, item_total_calc, total_calc):
def heur(
puzzle: EightPuzzle, item_total_calc: Callable, total_calc: Callable
) -> Union[int, float]:
"""
Heuristic template that provides the current and target position for each number
and the total function.
Expand Down Expand Up @@ -215,33 +210,33 @@ def heur(puzzle, item_total_calc, total_calc):
# admissible.


def h_manhattan(puzzle):
def h_manhattan(puzzle: EightPuzzle) -> Union[int, float]:
return heur(puzzle, lambda r, tr, c, tc: abs(tr - r) + abs(tc - c), lambda t: t)


def h_manhattan_lsq(puzzle):
def h_manhattan_lsq(puzzle: EightPuzzle) -> Union[int, float]:
return heur(
puzzle,
lambda r, tr, c, tc: (abs(tr - r) + abs(tc - c)) ** 2,
lambda t: sqrt(t),
)


def h_linear(puzzle):
def h_linear(puzzle: EightPuzzle) -> Union[int, float]:
return heur(
puzzle,
lambda r, tr, c, tc: sqrt(sqrt((tr - r) ** 2 + (tc - c) ** 2)),
lambda t: t,
)


def h_linear_lsq(puzzle):
def h_linear_lsq(puzzle: EightPuzzle) -> Union[int, float]:
return heur(
puzzle, lambda r, tr, c, tc: (tr - r) ** 2 + (tc - c) ** 2, lambda t: sqrt(t),
)


def solve_8_puzzle(board):
def solve_8_puzzle(board: List[List[int]]) -> None:
transformed_board = [[elem if elem else 0 for elem in row] for row in board]
p = EightPuzzle(transformed_board)
print(p)
Expand All @@ -261,9 +256,5 @@ def solve_8_puzzle(board):


if __name__ == "__main__":
board = [
[4, 1, 2],
[7, 5, 3],
[None, 8, 6]
]
board = [[4, 1, 2], [7, 5, 3], [None, 8, 6]]
solve_8_puzzle(board)

0 comments on commit 6f0eae5

Please sign in to comment.