Skip to content
Merged
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
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@
* [Test Largest Palindromic Number](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/largest_palindromic_number/test_largest_palindromic_number.py)
* Majority Element
* [Test Majority Element](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/majority_element/test_majority_element.py)
* Maximum Swap
* [Test Maximum Swap](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/maximum_swap/test_maximum_swap.py)
* Min Arrows
* [Test Find Min Arrows](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/min_arrows/test_find_min_arrows.py)
* Minimum Number Of Pushes
Expand Down
354 changes: 354 additions & 0 deletions algorithms/greedy/maximum_swap/README.md

Large diffs are not rendered by default.

101 changes: 101 additions & 0 deletions algorithms/greedy/maximum_swap/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from typing import List


def maximum_swap(num: int) -> int:
# Convert the number to a list of characters for easy manipulation
digits: List[str] = list(str(num))
n = len(digits)

# Variables to track the index of the maximum digit and the two swap indices
max_digit_index = index_1 = index_2 = -1

# Iterate the string from the last digit to the first
for i in range(n - 1, -1, -1):
# Update the max digit index if the current digit is greater
if max_digit_index == -1 or digits[i] > digits[max_digit_index]:
max_digit_index = i
# If the current digit is less than the max digit found so far,
# mark this index (index_1) and the max digit's index (index_2) for swapping
elif digits[i] < digits[max_digit_index]:
index_1 = i
index_2 = max_digit_index

# Perform the swap if valid indices are found
if index_1 != -1 and index_2 != -1:
digits[index_1], digits[index_2] = digits[index_2], digits[index_1]

# Convert the list back to an integer and return it
return int("".join(digits))


def maximum_swap_suboptimal_greedy(num: int) -> int:
num_str = str(num)
n = len(num_str)
last_seen = [-1] * 10 # Store the last occurrence of each digit

# Record the last occurrence of each digit
for i in range(n):
last_seen[int(num_str[i])] = i

# Traverse the string to find the first digit that can be swapped with a larger one
for i in range(n):
for d in range(9, int(num_str[i]), -1):
if last_seen[d] > i:
# Perform the swap
num_str = list(num_str)
num_str[i], num_str[last_seen[d]] = (
num_str[last_seen[d]],
num_str[i],
)
num_str = "".join(num_str)

return int(num_str) # Return the new number immediately after the swap

return num # Return the original number if no swap can maximize it


def maximum_swap_greedy_two_pass(num: int) -> int:
num_str = list(str(num))
n = len(num_str)
max_right_index = [0] * n

max_right_index[n - 1] = n - 1
for i in range(n - 2, -1, -1):
max_right_index[i] = (
i
if num_str[i] > num_str[max_right_index[i + 1]]
else max_right_index[i + 1]
)

for i in range(n):
if num_str[i] < num_str[max_right_index[i]]:
num_str[i], num_str[max_right_index[i]] = (
num_str[max_right_index[i]],
num_str[i],
)
return int("".join(num_str))

return num


def maximum_swap_brute_force(num: int) -> int:
num_str = str(num) # Convert num to string for easy manipulation
n = len(num_str)
max_num = num # Track the maximum number found

# Try all possible swaps
for i in range(n):
for j in range(i + 1, n):
num_list = list(num_str) # Convert the string to list for swapping

num_list[i], num_list[j] = (
num_list[j],
num_list[i],
) # Swap digits at index i and j
temp = int(
"".join(num_list)
) # Convert the list back to string and then to integer

max_num = max(max_num, temp) # Update max_num if the new number is larger

return max_num # Return the largest number after all possible swaps
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 44 additions & 0 deletions algorithms/greedy/maximum_swap/test_maximum_swap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import unittest
from parameterized import parameterized
from algorithms.greedy.maximum_swap import (
maximum_swap,
maximum_swap_suboptimal_greedy,
maximum_swap_greedy_two_pass,
maximum_swap_brute_force,
)

MAXIMUM_SWAP_TEST_CASES = [
(4121, 4211),
(87654, 87654),
(1643, 6143),
(123, 321),
(14, 41),
(2736, 7236),
(9973, 9973),
]


class MaximumSwapTestCase(unittest.TestCase):
@parameterized.expand(MAXIMUM_SWAP_TEST_CASES)
def test_maximum_swap(self, num: int, expected: int):
actual = maximum_swap(num)
self.assertEqual(expected, actual)

@parameterized.expand(MAXIMUM_SWAP_TEST_CASES)
def test_maximum_swap_suboptimal_greedy(self, num: int, expected: int):
actual = maximum_swap_suboptimal_greedy(num)
self.assertEqual(expected, actual)

@parameterized.expand(MAXIMUM_SWAP_TEST_CASES)
def test_maximum_swap_greedy_two_pass(self, num: int, expected: int):
actual = maximum_swap_greedy_two_pass(num)
self.assertEqual(expected, actual)

@parameterized.expand(MAXIMUM_SWAP_TEST_CASES)
def test_maximum_swap_brute_force(self, num: int, expected: int):
actual = maximum_swap_brute_force(num)
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion algorithms/heap/min_cost_to_connect_sticks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def connect_sticks_2(sticks: List[int]) -> int:
# Add the cost to the total cost
total_cost += cost
# Push the connected stick back into the heap
min_heap.insert_data(cost)
min_heap.insert(cost)

# Return the total cost
return total_cost
6 changes: 3 additions & 3 deletions algorithms/sorting/heapsort/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ time complexity of O(nlg(n)).

We've shown that the heapify step is O(nlg(n)). With a bit more complex analysis, it turns out to actually be O(n).

After transforming the tree into a heap, we remove all nn elements from it—one item at a time. Removing from a heap
After transforming the tree into a heap, we remove all n elements from it—one item at a time. Removing from a heap
takes O(lg(n)) time, since we have to move a new value to the root of the heap and bubble down. Doing n remove
operations will be O(nlg(n)) time.

Expand All @@ -24,9 +24,9 @@ Putting these steps together, we're at O(nlg(n)) time in the worst case (and on
But what happens if all the items in the input are the same?

Every time we remove an element from the tree root, the item replacing it won't have to bubble down at all. In that
case, each remove takes O(1)O(1) time, and doing nn remove operations takes O(n).
case, each remove takes O(1) time, and doing n remove operations takes O(n).

So the best case time complexity is O(n)O(n). This is the runtime when everything in the input is identical.
So the best case time complexity is O(n). This is the runtime when everything in the input is identical.

Since we cleverly reused available space at the end of the input list to store the item we removed, we only need O(1)O(
1) space overall for heapsort.
Loading