Skip to content

Commit

Permalink
new file: Additional Exercises/Easy 938. Range Sum of BST.py
Browse files Browse the repository at this point in the history
	new file:   Additional Exercises/Medium 215. Kth Largest Element in an Array.py
	new file:   Additional Exercises/Medium 791. Custom Sort String.py
	modified:   README.md
  • Loading branch information
open-minded13 committed Mar 14, 2024
1 parent 2f315dc commit 96cf247
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 0 deletions.
67 changes: 67 additions & 0 deletions Additional Exercises/Easy 938. Range Sum of BST.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from typing import Optional

# Date of Last Practice: Mar 13, 2024
#
# Time Complexity: O(N), where N is the number of nodes in the binary search tree (BST).
# In the worst-case scenario, the algorithm might
# need to visit every node in the tree once.
#
# Space Complexity: O(H), where H is the height of the tree.
# This space is used by the call stack
# during the recursive traversal of the tree.
# In the worst-case scenario (a degenerate tree),
# the space complexity can become O(N).


class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right


class Solution:
def __init__(self):
self.sum = 0

def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int:
# Helper function to perform DFS and sum values within range
def _range_sum(node):
if node is None: # Base case: Node is null
return
if low <= node.val <= high: # Node value is within range
self.sum += node.val
_range_sum(node.left) # Check left subtree
_range_sum(node.right) # Check right subtree
elif node.val < low: # Node value is less than range, skip left subtree
_range_sum(node.right)
elif (
node.val > high
): # Node value is greater than range, skip right subtree
_range_sum(node.left)

_range_sum(root) # Initialize DFS from root
return self.sum


# Test cases
solution = Solution()

# Test case 1
root1 = TreeNode(
10, TreeNode(5, TreeNode(3), TreeNode(7)), TreeNode(15, None, TreeNode(18))
)
assert solution.rangeSumBST(root1, 7, 15) == 32, "Test case 1 failed"

# Reset sum for the next test
solution.sum = 0

# Test case 2
root2 = TreeNode(
10,
TreeNode(5, TreeNode(3, TreeNode(1)), TreeNode(7, None, TreeNode(6))),
TreeNode(15, TreeNode(13), TreeNode(18)),
)
assert solution.rangeSumBST(root2, 6, 10) == 23, "Test case 2 failed"

print("All test cases passed!")
114 changes: 114 additions & 0 deletions Additional Exercises/Medium 215. Kth Largest Element in an Array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import heapq

# Date of Last Practice: Mar 13, 2024
#
# Time Complexity: O(N * logK), where N is the total elements in nums, and K is the size.
# The main operations involved are heap insertions (heapq.heappush)
# and deletions (heapq.heappop). The solution iterates through
# each of the N elements in the given array,
# performing a heap insertion for each.
# If the heap size exceeds K, it also performs a deletion.
#
# Space Complexity: O(K), where K is the size.
# The solution maintains a heap of size k.
# Therefore, the space complexity is O(k) for storing these elements.


class Solution:
def findKthLargest(self, nums, k):
# Step 1 - Initialize an empty heap
heap_list = []

# Step 2 - Iterate through all numbers in the array
for index, num in enumerate(nums):
# Step 3 - Maintain a heap of size k
heapq.heappush(heap_list, (num, index))
if len(heap_list) > k:
heapq.heappop(heap_list)

# Step 4 - The root of the heap is the kth largest element
kth_largest, _ = heapq.heappop(heap_list)
return kth_largest


class QuickSelectSolution:
# Reference: https://www.youtube.com/watch?v=XEmy13g1Qxc
#
# Time Complexity: O(N) in average and (N^2) in the worst case.
#
# In the average case, the partitioning step divides
# the array into two parts that are roughly equal in size.
# This means that each recursive call of _quick_select
# operates on half the size of the array compared to the previous call.
#
# The time complexity can thus be described as:
#
# The first call processes the entire array: O(N).
# The second call processes half of that: O(N/2).
# The third call processes a quarter: O(N/4).
# And so on...
#
# This series continues until the size of the processed array reduces to 1,
# forming a series: O(N) + O(N/2) + O(N/4) + O(N/8) + ... which converges to O(2N) = O(N).
#
# The worst case can happen if the array is already sorted
# (either in ascending or descending order), and
# the pivot chosen is either the smallest or largest element each time.
#
# In such cases, the partitioning does not effectively reduce the problem size, leading to:
# The first call processes the entire array: O(N).
# The second call processes N-1 elements: O(N-1).
# The third call processes N-2 elements: O(N-2).
# And so on until there's only 1 element left.
#
# Summing these up gives a series that approximates O((N+1)*N/2), which simplifies to O(N^2).

def findKthLargest(self, nums, k):
k = len(nums) - k

def _quick_select(left, right):
pivot_index, partition_index = right, left
for i in range(left, right):
if nums[i] <= nums[pivot_index]:
nums[partition_index], nums[i] = nums[i], nums[partition_index]
partition_index += 1
nums[partition_index], nums[pivot_index] = (
nums[pivot_index],
nums[partition_index],
)

if partition_index == k:
return nums[partition_index]
elif partition_index > k:
return _quick_select(left, partition_index - 1)
else:
return _quick_select(partition_index + 1, right)

return _quick_select(0, len(nums) - 1)


# Test cases
solution = Solution()

# Test Case 1
assert solution.findKthLargest([3, 2, 1, 5, 6, 4], 2) == 5, "Test case 1 failed"

# Test Case 2
assert (
solution.findKthLargest([3, 2, 3, 1, 2, 4, 5, 5, 6], 4) == 4
), "Test case 2 failed"

print("All test cases passed!")

# Test cases
solution = QuickSelectSolution()

# Test Case 1
assert solution.findKthLargest([3, 2, 1, 5, 6, 4], 2) == 5, "Test case 1 failed"

# Test Case 2
assert (
solution.findKthLargest([3, 2, 3, 1, 2, 4, 5, 5, 6], 4) == 4
), "Test case 2 failed"

print("All test cases passed!")
45 changes: 45 additions & 0 deletions Additional Exercises/Medium 791. Custom Sort String.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from collections import Counter

# Date of Last Practice: Mar 13, 2024
#
# Time Complexity: O(N+M), where N is the length of the input string s, and M is the length of order.
# The first loop iterates over each character in s, which is O(N).
# The second loop iterates over each character in order, which is O(m).
# Since these steps are sequential and not nested,
# the overall time complexity is O(N+M).
#
# Space Complexity: O(1) (or O(N) if considering the final result list).
# This uses additional space proportional to
# the number of unique characters in s,
# which is at most O(26) if all characters are unique.


class Solution:
def customSortString(self, order: str, s: str) -> str:
# Step 1: Count occurrences of each character in s
letter_count = Counter(s)

result = []

# Step 2: Append characters in the order of 'order'
for char in order:
result.append(char * letter_count[char])
del letter_count[char]

# Step 3: Append characters not in 'order'
for char in letter_count.keys():
result.append(char * letter_count[char])

return "".join(result)


# Test cases
sol = Solution()
assert sol.customSortString("cba", "abcd") == "cbad"
assert sol.customSortString("bcafg", "abcd") == "bcad"
# Testing with characters not in 'order'
assert sol.customSortString("xyz", "abcabc") == "aabbcc"
# Testing with repeating characters in 's'
assert sol.customSortString("cba", "aabbccdd") == "ccbbaadd"

print("All tests passed!")
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,9 @@ Current Progress: 72/75.
- [x] [Medium 1570. Dot Product of Two Sparse Vectors](https://leetcode.com/problems/dot-product-of-two-sparse-vectors/)
- [x] [Medium 1650. Lowest Common Ancestor of a Binary Tree III](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iii/) | Algo: Depth-First Search
- [x] [Medium 1762. Buildings with an Ocean View](https://leetcode.com/problems/buildings-with-an-ocean-view/description/) | DS: Monotonic Stack

Sun:
- [x] [Medium 215. Kth Largest Element in an Array](https://leetcode.com/problems/buildings-with-an-ocean-view/description/) | Algo: Quickselect (Fastest in Average Cases), DS: Heap (Good in All Cases)
- [x] [Easy 938. Range Sum of BST](https://leetcode.com/problems/range-sum-of-bst/)
- [x] [Medium 791. Custom Sort String](https://leetcode.com/problems/custom-sort-string/description/)
- [x] [Medium 426. Convert Binary Search Tree to Sorted Doubly Linked List](https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/description/)

0 comments on commit 96cf247

Please sign in to comment.