├── .gitignore
├── LICENSE
├── README.md
├── arr n hashing
├── anagram.py
├── duplicates.py
├── feature_sort_popularity.py
├── gp_shifted_strings.py
├── gpanagrams.py
├── longest_consecutive.py
└── top_k_freq_elmnt.py
├── binary search
├── bs overview.ipynb
├── ctci
│ ├── peak_valley.py
│ └── rank_stream.py
├── implicitly sorted array
│ ├── minimum_rotated_sorted.py
│ ├── peek_mountain.py
│ ├── remove_water.py
│ └── search_rotated_arr.py
├── sorted array
│ ├── binary_search_unknown_size.py
│ ├── bs.py
│ ├── first_last_occurance.py
│ ├── pow.py
│ └── sqrt_root.py
└── story_based_bs
│ ├── koko_banana.py
│ ├── ship_within_days.py
│ └── sparse_search.py
├── dfs
├── 01 intro.ipynb
├── 02 tree intro.ipynb
├── 03 dfs infra.ipynb
├── 04 dfs on tree.ipynb
└── leetcode
│ ├── balanced_tree.py
│ ├── employee_inform_time.py
│ ├── encode_str.py
│ ├── good_points.py
│ ├── lca.py
│ ├── longest_consecutive_path.py
│ ├── max_depth.py
│ ├── n-array-tree_depth.py
│ ├── readme.md
│ ├── serialize_tree.py
│ ├── smallest_region.py
│ └── validate_BST.py
├── heap
├── 01 Heap fundamental.ipynb
├── 02 heap definition.ipynb
├── 03 heap operations.ipynb
├── imgs
│ ├── heap.png
│ ├── insert.png
│ ├── minBH.png
│ └── minBHrep.png
└── leetcode
│ ├── moving best
│ ├── re_organize_str.py
│ ├── task_scheduler.py
│ └── ugly_num.py
│ ├── readme.md
│ └── topk
│ ├── char_by_freq.py
│ ├── k_closest_toOrigin.py
│ ├── k_freq_words.py
│ ├── k_largest_array.py
│ └── largest_word_count_msg.py
├── img
├── 1.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
├── 6.png
├── 7.png
├── Binary_Search.svg
├── amz.png
├── binary_search.001.png
├── dfs.png
├── dfs_on_trees_intro_1.png
├── dfs_on_trees_intro_2.png
├── fb.png
├── goo.png
├── inorder_traversal.gif
├── postorder_traversal.gif
├── preorder_traversal.gif
├── r.png
├── stack.png
├── stats.png
└── tree_intro.001.png
└── stack
├── 01 intro.ipynb
├── queue.py
└── stack n queue intro.ipynb
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Shirin Yamani
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # algo-monster
2 |
3 |
4 |
5 |
6 |
7 | # What to study?
8 |
9 | Here is the ROI table based on the above analysis for your reference;
10 |
11 |
12 |
--------------------------------------------------------------------------------
/arr n hashing/anagram.py:
--------------------------------------------------------------------------------
1 | from collections import Counter
2 |
3 | class Solution(object):
4 | def isAnagram(self, s, t):
5 | if len(s) != len(t):
6 | return False
7 | elif len(s) == len(t):
8 |
9 | for ch in range(len(s)):
10 | countS = Counter(s)
11 | countT = Counter(t)
12 |
13 | return countS == countT
--------------------------------------------------------------------------------
/arr n hashing/duplicates.py:
--------------------------------------------------------------------------------
1 | # approach 1: Dict/Counter
2 | import collections
3 |
4 | #Approach 1: Dict/Counter
5 | def containDuplicates(nums):
6 | count = collections.Counter(nums)
7 | for num in nums:
8 | if count[num] > 1:
9 | return True
10 | else: False
11 |
12 | #Approach 2: Set
13 | def containDuplicates(nums):
14 | hash_set = set()
15 | for num in nums:
16 | if num in hash_set:
17 | return True
18 | hash_set.add(num)
19 |
20 | return False
--------------------------------------------------------------------------------
/arr n hashing/feature_sort_popularity.py:
--------------------------------------------------------------------------------
1 | import collections
2 |
3 | def sort_features(features, responses):
4 | # freq ---> default dict (list) features --- responses
5 | # response split
6 | # order ---> i , word (enumerate)
7 | feature_set = set(features)
8 | freq = collections.defaultdict(int) #{feature:[num]}
9 | order = {word: i for i, word in enumerate(features)}
10 | for r in responses:
11 | for f in set(r.split(' ')): # bc if repeated word, it will be counted twice
12 | if f in feature_set:
13 | freq[f] += 1 # {feature:num} for the sort purpose
14 |
15 | features.sort(key= lambda x: (-freq[x], order[x])) # - freq[x] for descending order
16 | return features
17 |
18 |
19 |
20 | if __name__ == "__main__":
21 | print(sort_features(["easy", "touch"], ["i love the touch pad touch", "touch it was not easy to use"]))
--------------------------------------------------------------------------------
/arr n hashing/gp_shifted_strings.py:
--------------------------------------------------------------------------------
1 | import collections
2 |
3 | def groupShiftedStrings(strs):
4 | shifted = collections.defaultdict(list) #{key: [values]}
5 | for s in strs:
6 | key = ""
7 | for ch in range(1, len(s)):
8 | key += str(ord(s[ch]) - ord(s[ch - 1])) # ord() returns the ascii value of a character
9 | print(key)
10 |
11 | shifted[key].append(s)
12 |
13 | return shifted.values()
14 |
15 |
16 | if __name__ == "__main__":
17 | print(groupShiftedStrings(["acd", "dfg", "wyz", "yab", "mop",
18 | "bdfh", "a", "x", "moqs"]))
--------------------------------------------------------------------------------
/arr n hashing/gpanagrams.py:
--------------------------------------------------------------------------------
1 | import collections
2 |
3 | class Solution(object):
4 | def groupAnagrams(self, strs):
5 | """
6 | :type strs: List[str]
7 | :rtype: List[List[str]]
8 | """
9 | anagram = collections.defaultdict(list) #{key: [value]}
10 |
11 | for s in strs:
12 | res = ''.join(sorted(s)) # sort the string for each word for searching the char
13 | if res in anagram:
14 | anagram[res].append(s) # if the sorted string is already in the dictionary, append the word to the list
15 |
16 | else:
17 | anagram[res] = [s] # if the sorted string is not in the dictionary, create a new key and value pair
18 |
19 | return anagram.values()
20 |
--------------------------------------------------------------------------------
/arr n hashing/longest_consecutive.py:
--------------------------------------------------------------------------------
1 | def longestConsecutive(self, nums):
2 | nums_set = set(nums)
3 | longest = 0
4 |
5 | for num in nums:
6 | if (num - 1) not in nums_set:
7 | length = 1
8 | while (num + length) in nums_set:
9 | length += 1
10 | longest = max(length, longest)
11 |
12 | return longest
13 |
--------------------------------------------------------------------------------
/arr n hashing/top_k_freq_elmnt.py:
--------------------------------------------------------------------------------
1 | # dic/ counter for couting the elmnt-freq
2 | # heap for efficinecy
3 | from collections import Counter
4 | import heapq
5 |
6 | def top_k_element(nums, k):
7 | count = Counter(nums) #{elmnt: freq}
8 | heap = []
9 | heapq.heapify(heap)
10 | for elm, freq in count.items():
11 | heapq.heappush(heap, [-freq, elm])
12 |
13 | res = []
14 | for i in range(k):
15 | out = heapq.heappop(heap)
16 | res.append(out[1])
17 | return res
18 |
19 |
20 | if __name__ == "__main__":
21 | print(top_k_element([1,1,1,2,2,3], 2))
22 |
--------------------------------------------------------------------------------
/binary search/bs overview.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Binary Search\n",
8 | "\n",
9 | "\n",
10 | "Binary search is an efficient array search algorithm. It works by narrowing down the search range by half each time. If you have looked up a word in a physical dictionary, you've already used binary search in real life. Let's look at a simple example:\n",
11 | "\n",
12 | "Given a sorted array of integers and an integer called target, find the element that equals the target and return its index. If the element is not found, return -1.\n",
13 | "\n",
14 | "The key observation here is that the array is sorted. We pick a random element in the array and compare it to the target.\n",
15 | "\n",
16 | "- If we happen to pick the element that equals the target (how lucky!), then bingo. We don't need to do any more work; return its index.\n",
17 | "- If the element is smaller than the target, then we know the target cannot be found in the section to the left of the current element since everything to the left is even smaller. So we discard the current element and everything on the left from the search range.\n",
18 | "- If the element is larger than the target, then we know the target cannot be found in the section to the right of the current element since everything to the right is even larger. So we discard the current element and everything on the right from the search range.\n",
19 | "\n",
20 | "We repeat this process until we find the target. Instead of picking a random element, we always pick the middle element in the current search range. This way, we can discard half of the options and shrink the search range by half each time. This gives us O(log(N)) runtime\n",
21 | "\n",
22 | "# Implementation\n",
23 | "The search range is represented by the left and right indices that start from both ends of the array and move towards each other as we search. When moving the index, we discard elements and shrink the search range. \n",
24 | "\n",
25 | "`Time Complexity: O(log(n))`\n",
26 | "\n",
27 | "\n",
28 | "\n"
29 | ]
30 | },
31 | {
32 | "cell_type": "code",
33 | "execution_count": 1,
34 | "metadata": {},
35 | "outputs": [],
36 | "source": [
37 | "def BS(arr, target):\n",
38 | " left, right = 0, len(arr) - 1\n",
39 | " while left <= right: # <= because left and right could point to the same element, < would miss it\n",
40 | " mid = (left + right) // 2 # double slash for integer division in python 3, we don't have to worry about integer `left + right` overflow since python integers can be arbitrarily large\n",
41 | " # found target, return its index\n",
42 | " if arr[mid] == target:\n",
43 | " return mid\n",
44 | " # middle less than target, discard left half by making left search boundary `mid + 1`\n",
45 | " if arr[mid] < target:\n",
46 | " left = mid + 1\n",
47 | " # middle greater than target, discard right half by making right search boundary `mid - 1`\n",
48 | " else:\n",
49 | " right = mid - 1\n",
50 | " return -1 # if we get here we didn't hit above return so we didn't find target"
51 | ]
52 | },
53 | {
54 | "cell_type": "markdown",
55 | "metadata": {},
56 | "source": [
57 | "# Calculating `mid`\n",
58 | "Note that when calculating mid, if the number of elements is even, there are two elements in the middle. We usually follow the convention of picking the first one, equivalent to doing integer division (left + right) / 2.\n",
59 | "\n",
60 | "In most programming languages, we calculate mid with left + floor((right-left) / 2) to avoid potential integer overflow. However, in Python, we do not need to worry about left + right integer overflow because Python3 integers can be arbitrarily large.\n",
61 | "\n",
62 | "# Deducing binary search\n",
63 | "It's essential to understand and deduce the algorithm yourself instead of memorizing it. In an actual interview, interviewers may ask you additional questions to test your understanding, so simply memorizing the algorithm may not be enough to convince the interviewer.\n",
64 | "\n",
65 | "# Key elements in writing a correct binary search:\n",
66 | "\n",
67 | "1. When to terminate the loop\n",
68 | "Make sure the while loop has an equality comparison. Otherwise, we'd skip the loop and miss the potential match for the edge case of a one-element array.\n",
69 | "\n",
70 | "2. Whether/how to update left and right boundary in the if conditions\n",
71 | "Consider which side to discard. If arr[mid] is already smaller than the target, we should discard everything on the left by making left = mid + 1.\n",
72 | "\n",
73 | "3. Should I discard the current element?\n",
74 | "For vanilla binary search, we can discard it since it can't be the final answer if it is not equal to the target. There might be situations where you would want to think twice before discarding the current element. We'll discuss this in the next module.\n",
75 | "\n",
76 | "# When to use binary search\n",
77 | "Interestingly, binary search works beyond sorted arrays. You can use binary search whenever you make a binary decision to shrink the search range. We will see this in the following modules."
78 | ]
79 | },
80 | {
81 | "cell_type": "markdown",
82 | "metadata": {},
83 | "source": []
84 | }
85 | ],
86 | "metadata": {
87 | "kernelspec": {
88 | "display_name": "Python 3.10.4 64-bit",
89 | "language": "python",
90 | "name": "python3"
91 | },
92 | "language_info": {
93 | "codemirror_mode": {
94 | "name": "ipython",
95 | "version": 3
96 | },
97 | "file_extension": ".py",
98 | "mimetype": "text/x-python",
99 | "name": "python",
100 | "nbconvert_exporter": "python",
101 | "pygments_lexer": "ipython3",
102 | "version": "3.10.4"
103 | },
104 | "orig_nbformat": 4,
105 | "vscode": {
106 | "interpreter": {
107 | "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
108 | }
109 | }
110 | },
111 | "nbformat": 4,
112 | "nbformat_minor": 2
113 | }
114 |
--------------------------------------------------------------------------------
/binary search/ctci/peak_valley.py:
--------------------------------------------------------------------------------
1 | from os import TMP_MAX
2 | from tempfile import tempdir
3 |
4 |
5 | def sortValleyPeak(arr):
6 | for i in range(1, len(arr)):
7 | biggest = max_index(arr, i-1, i, i+1)
8 | if biggest != i:
9 | swap(arr, i, biggest)
10 | i += 2
11 |
12 |
13 |
14 | def max_index(arr, left, mid, right): #max(arr[left], max(arr[mid], arr[right]))
15 | if left == right:
16 | return left
17 | elif left == mid:
18 | return mid
19 |
20 | elif right == mid:
21 | return mid
22 |
23 | elif left > mid and right < mid:
24 | swap(arr, left, mid)
25 |
26 | elif left < mid and right > mid:
27 | swap(arr, right, mid)
28 |
29 |
30 | def swap(arr, left, right):
31 | temp = arr[left]
32 | arr[left] = arr[right]
33 | arr[right] = temp
34 |
35 |
--------------------------------------------------------------------------------
/binary search/ctci/rank_stream.py:
--------------------------------------------------------------------------------
1 | class Node:
2 | def __init__(self, val):
3 | self.val = val
4 | self.left = None
5 | self.right = None
6 | self.leftSize = 0
7 |
8 | def insert(root, val): # O(log n) # BC insertion in arr is O(n)
9 | if root is None:
10 | return Node(val)
11 |
12 | elif val < root.val:
13 | root.left = insert(root.left, val)
14 | root.leftSize += 1
15 | else:
16 | root.right = insert(root.right, val)
17 |
18 | return root
19 |
20 | def get_rank(root, x):
21 | if root.val == x:
22 | return root.leftSize
23 |
24 | elif x < root.val:
25 | if root.left is None:
26 | return -1
27 | else:
28 | return get_rank(root.left, x)
29 | else:
30 | if root.right is None:
31 | return -1
32 | else:
33 | return root.leftSize + 1 + get_rank(root.right, x)
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/binary search/implicitly sorted array/minimum_rotated_sorted.py:
--------------------------------------------------------------------------------
1 | def min_sorted_arr(nums):
2 | res = nums[0]
3 | left, right = 0, len(nums) - 1
4 | while left <= right:
5 | if nums[left] < nums[right]:
6 | res = min(res, nums[left]) #bc the nums[left] itself can be the min
7 | break
8 |
9 | mid = (left + right) // 2
10 | if nums[mid] >= nums[left]:
11 | left = mid + 1
12 |
13 | else:
14 | right = mid - 1
15 |
16 | return res
17 |
--------------------------------------------------------------------------------
/binary search/implicitly sorted array/peek_mountain.py:
--------------------------------------------------------------------------------
1 | def peakIndexInMountainArray(self, arr):
2 | lo, hi = 0, len(arr) - 1
3 | while lo < hi:
4 | mi = (lo + hi) / 2
5 | if arr[mi] < arr[mi + 1]:
6 | lo = mi + 1
7 | else:
8 | hi = mi
9 | return lo
10 |
--------------------------------------------------------------------------------
/binary search/implicitly sorted array/remove_water.py:
--------------------------------------------------------------------------------
1 | def remove_water_litres(arr):
2 | pass
3 |
--------------------------------------------------------------------------------
/binary search/implicitly sorted array/search_rotated_arr.py:
--------------------------------------------------------------------------------
1 | def search_rotared_arr(nums, target):
2 | left, right = 0, len(nums) - 1
3 | while left <= right:
4 | mid = (left + right) // 2
5 | if nums[mid] == target:
6 | return mid
7 |
8 | # left portion is sorted
9 | if nums[left] <= nums[mid]:
10 | if target > nums[mid] or target < nums[left]:
11 | left = mid + 1
12 | else:
13 | right = mid - 1
14 |
15 |
16 | #right portion is sorted
17 | else:
18 | if target < nums[mid] or target > nums[right]:
19 | right = mid -1
20 |
21 | else:
22 | left = mid + 1
23 |
24 |
25 | return -1
26 |
--------------------------------------------------------------------------------
/binary search/sorted array/binary_search_unknown_size.py:
--------------------------------------------------------------------------------
1 |
2 | def search_unknown_size(reader, target):
3 | left = 0
4 | right = reader.next()
5 | while left < right:
6 | mid = (left + right) // 2
7 | if reader.get(mid) == target:
8 | return mid
9 | elif reader.get(mid) < target:
10 | left = mid + 1
11 | else:
12 | right = mid - 1
13 | return -1
14 |
15 |
16 | def search_unknown_size(arr, target):
17 | i = 1
18 | while ArrayReader.get(i) < target:
19 | i = i * 2
20 | return binary_search(arr, target, i // 2, i)
21 |
22 | def binary_search(arr, target, left, right):
23 | while left <= right:
24 | mid = (left + right) // 2
25 | mid = ArrayReader.get(mid)
26 |
27 | if arr[mid] <= target:
28 | left = mid + 1
29 |
30 | elif arr[mid] > target:
31 | right = mid - 1
32 |
33 | else: return -1
--------------------------------------------------------------------------------
/binary search/sorted array/bs.py:
--------------------------------------------------------------------------------
1 | def binary_search(arr, target):
2 | left = 0
3 | right = len(arr) - 1
4 | while left <= right:
5 | mid = (left + right) // 2
6 | if arr[mid] == target:
7 | return mid
8 | elif arr[mid] < target:
9 | left = mid + 1
10 | else:
11 | right = mid - 1
12 | return -1
--------------------------------------------------------------------------------
/binary search/sorted array/first_last_occurance.py:
--------------------------------------------------------------------------------
1 | # Approach 1
2 | def search_range(arr, target):
3 | res = []
4 | if (arr.count(target) == 0):
5 | return [-1, -1]
6 | res.append(arr.index(target))
7 | for i in range(len(arr)-1,-1,-1):
8 | if (arr[i] == target):
9 | res.append(i)
10 | break
11 |
12 | return res
13 |
14 |
15 | # Approach 2: Binary Search
16 | def searchRange(nums, target):
17 | left = binSearch(nums, target, True)
18 | right = binSearch(nums, target, False)
19 | return [left, right]
20 |
21 | # leftBias=[True/False], if false, res is rightBiased
22 | def binSearch(nums, target, leftBias):
23 | l, r = 0, len(nums) - 1
24 | i = -1
25 | while l <= r:
26 | m = (l + r) // 2
27 | if target > nums[m]:
28 | l = m + 1
29 | elif target < nums[m]:
30 | r = m - 1
31 | else:
32 | i = m
33 | if leftBias:
34 | r = m - 1
35 | else:
36 | l = m + 1
37 | return i
--------------------------------------------------------------------------------
/binary search/sorted array/pow.py:
--------------------------------------------------------------------------------
1 | def myPow(self, x, n):
2 | """
3 | :type x: float
4 | :type n: int
5 | :rtype: float
6 | """
7 | if not n:
8 | return 1
9 | if n < 0:
10 | return 1 / self.myPow(x, -n)
11 | if n % 2:
12 | return x * self.myPow(x, n-1)
13 | return self.myPow(x*x, n/2)
--------------------------------------------------------------------------------
/binary search/sorted array/sqrt_root.py:
--------------------------------------------------------------------------------
1 | def sqrt(x):
2 | left, right = 0, x
3 | while left <= right:
4 | mid = (left + right) // 2
5 |
6 | if mid * mid == x:
7 | return mid
8 |
9 | elif mid * mid < x:
10 | left = mid + 1
11 |
12 | else:
13 | right = mid - 1
14 |
15 | return right
16 |
17 |
--------------------------------------------------------------------------------
/binary search/story_based_bs/koko_banana.py:
--------------------------------------------------------------------------------
1 | def min_banana(piles, h):
2 | l, r = 1, max(piles)
3 | k = 0
4 |
5 | while l <= r:
6 | m = (l + r) // 2
7 |
8 | totalTime = 0
9 | for p in piles:
10 | totalTime += ((p - 1) // m) + 1
11 | if totalTime <= h:
12 | k = m
13 | r = m - 1
14 | else:
15 | l = m + 1
16 | return k
--------------------------------------------------------------------------------
/binary search/story_based_bs/ship_within_days.py:
--------------------------------------------------------------------------------
1 | def shipwithinDays(self, weights, days):
2 | """
3 | :type weights: List[int]
4 | :type D: int
5 | :rtype: int
6 | """
7 | if days == 1:
8 | return sum(weights)
9 |
10 | def minCapacity(capacity):
11 | total,cur_day = 0, 1
12 | for w in weights:
13 | total += w
14 |
15 | if total > capacity:
16 | total = w
17 | cur_day += 1
18 |
19 | if cur_day > days:
20 | return False
21 |
22 | return True
23 |
24 | left, right = max(weights), sum(weights)
25 | while left < right:
26 | mid = (left + right) // 2
27 | if minCapacity(mid):
28 | right = mid
29 | else: left = mid + 1
30 | return left
--------------------------------------------------------------------------------
/binary search/story_based_bs/sparse_search.py:
--------------------------------------------------------------------------------
1 | def sparse_search(strs, target):
2 | pass
3 |
--------------------------------------------------------------------------------
/dfs/01 intro.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Recursion \n",
8 | "\n",
9 | "The process of a function calling itself on n on on! In the following exp the act of **\"puting someone on hold to ask another person\"** is the concept of recursion\n",
10 | "\n",
11 | ""
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": 2,
17 | "metadata": {},
18 | "outputs": [],
19 | "source": [
20 | "def call_for_lunch(person):\n",
21 | " if person == \"John\": #base condition\n",
22 | " return True\n",
23 | " else: call_for_lunch(nxt_person) #recursion occurance\n",
24 | "\n",
25 | "\n",
26 | "# Classical TextBook exp:\n",
27 | "def fibo(num):\n",
28 | " if num <= 1: #base condition\n",
29 | " return 1\n",
30 | " else: fibo(num-1) + fibo(num-2) #function with different inputs"
31 | ]
32 | },
33 | {
34 | "cell_type": "markdown",
35 | "metadata": {},
36 | "source": [
37 | "# Recursion and Stack\n",
38 | "\n",
39 | "How does a PC understand such a thing as a function reapiting itself? The answer is simple-- via Stack! Why? to keep track of where things are!"
40 | ]
41 | },
42 | {
43 | "cell_type": "code",
44 | "execution_count": null,
45 | "metadata": {},
46 | "outputs": [],
47 | "source": [
48 | "def fibo_stack(num):\n",
49 | " stack = []\n",
50 | " # push each call to a stack\n",
51 | " # top of the stack is base case\n",
52 | " while num > 1:\n",
53 | " stack.append(num)\n",
54 | " num -= 1\n",
55 | "\n",
56 | " res = 1\n",
57 | " # pop and use return value until stack is empty\n",
58 | " while stack is not None:\n",
59 | " res *= stack.pop()\n",
60 | "\n",
61 | " return res\n"
62 | ]
63 | }
64 | ],
65 | "metadata": {
66 | "kernelspec": {
67 | "display_name": "Python 3.10.4 64-bit",
68 | "language": "python",
69 | "name": "python3"
70 | },
71 | "language_info": {
72 | "codemirror_mode": {
73 | "name": "ipython",
74 | "version": 3
75 | },
76 | "file_extension": ".py",
77 | "mimetype": "text/x-python",
78 | "name": "python",
79 | "nbconvert_exporter": "python",
80 | "pygments_lexer": "ipython3",
81 | "version": "3.10.4"
82 | },
83 | "orig_nbformat": 4,
84 | "vscode": {
85 | "interpreter": {
86 | "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
87 | }
88 | }
89 | },
90 | "nbformat": 4,
91 | "nbformat_minor": 2
92 | }
93 |
--------------------------------------------------------------------------------
/dfs/02 tree intro.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "## Introduction to Tree Data Structure\n",
8 | "\n",
9 | "A tree is a data structure composed of nodes connected by edges, where each node can have any data type as values. Each node in a tree can be connected to zero or more child nodes, depending on the type of tree, but must be connected to exactly one parent node. The only exception is the root node, which has no parent, and every tree has only one root node. These constraints mean that a tree has no cycles or loops.\n",
10 | "\n",
11 | "Below are some common terminologies related to trees:\n",
12 | "\n",
13 | "- Internal node: every node in a tree which is not the root node.\n",
14 | "- Leaf node: every node in a tree that has no child nodes.\n",
15 | "- Ancestor: all the nodes that are between the path from the root to the current node are the ancestors of the current node. An ancestor node of the current node is either the parent of the current node or the parent of another ancestor of the node.\n",
16 | "- Descendent: all the nodes that are reachable from the current node when moving down the tree are the descendants of the current node. A descendant of the current node is either a child of the node or a child of another descendant of the node.\n",
17 | "- Level: the level of a node the number of ancestors from that node until the root node. The root node is at level 0.\n",
18 | "- Depth: the depth of a node is the number of edges on the path from the root to that node.\n",
19 | "- Height: the height of a node is the number of edges on the longest path from that node to a leaf. The height of a tree is the height of its root node.\n",
20 | "\n",
21 | ""
22 | ]
23 | },
24 | {
25 | "cell_type": "markdown",
26 | "metadata": {},
27 | "source": [
28 | "# Tree Traversal\n",
29 | "## In-order Traversal\n",
30 | "In-order traversal visits the left branch first, then the current node, and finally the right branch. The diagram below shows the traversal order of an in-order traversal on a binary tree.\n",
31 | "\n",
32 | "\n",
33 | "\n",
34 | "## Pre-order Traversal\n",
35 | "Pre-order traversal visits the current node first, then the left subtree, and finally the right subtree. The diagram below shows the traversal order of a pre-order traversal on a binary tree\n",
36 | "\n",
37 | "\n",
38 | "\n",
39 | "## Post-order Traversal\n",
40 | "Post-order traversal visits the left subtree first, then the right subtree, and finally the current node. The diagram below shows the traversal order of a post-order traversal on a binary tree.\n",
41 | "\n",
42 | ""
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": null,
48 | "metadata": {},
49 | "outputs": [],
50 | "source": [
51 | "class Tree:\n",
52 | " def __inint__(self, left=None, right=None, val=None):\n",
53 | " self.val = val\n",
54 | " self.right = right\n",
55 | " self.left = left\n",
56 | "\n",
57 | "def inOrder(root):\n",
58 | " if root is not None:\n",
59 | " inOrder(root.left)\n",
60 | " print(root.val)\n",
61 | " inOrder(root.right)\n",
62 | "\n",
63 | "def preOrder(root):\n",
64 | " if root is not None:\n",
65 | " print(root.val)\n",
66 | " preOrder(root.left)\n",
67 | " preOrder(root.right)\n",
68 | "\n",
69 | "\n",
70 | "def postOrder(root):\n",
71 | " if root is not None:\n",
72 | " postOrder(root.left)\n",
73 | " postOrder(root.right)\n",
74 | " postOrder(root.val)\n",
75 | "\n",
76 | "#Build tree from preorder and inorder \n",
77 | "def buildTree(preorder, inorder):\n",
78 | " if len(preorder) == 0 or len(inorder) == 0:\n",
79 | " return None\n",
80 | " root = Tree(val=preorder[0])\n",
81 | " index = inorder.index(preorder[0])\n",
82 | " root.left = buildTree(preorder[1:index+1], inorder[:index])\n",
83 | " root.right = buildTree(preorder[index+1:], inorder[index+1:])\n",
84 | " return root\n",
85 | " \n",
86 | "\n",
87 | "\n",
88 | " "
89 | ]
90 | },
91 | {
92 | "cell_type": "markdown",
93 | "metadata": {},
94 | "source": []
95 | }
96 | ],
97 | "metadata": {
98 | "language_info": {
99 | "name": "python"
100 | },
101 | "orig_nbformat": 4
102 | },
103 | "nbformat": 4,
104 | "nbformat_minor": 2
105 | }
106 |
--------------------------------------------------------------------------------
/dfs/03 dfs infra.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "\n",
8 | "\n",
9 | "## Prerequsite: Please check [this](./01%20intro.ipynb) doc before moving on!\n",
10 | "\n",
11 | "# DFS\n",
12 | "\n",
13 | "With a solid understanding of recursion under our belt, we are now ready to tackle one of the most useful technique in coding interviews - Depth First Search (DFS). As the name suggests, DFS is a bold search. We go as deep as we can to look for a value, and when there is nothing new to discover, we retrace our steps to find something new. To put it in a term we already know, the **pre-order traversal** of a tree is **DFS**. Let's look at a simple problem of searching for a node in a binary tree whose value is equal to target.\n"
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": 1,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "def dfs(root, target):\n",
23 | " if root is None:\n",
24 | " return None\n",
25 | " if root.val == target:\n",
26 | " return root\n",
27 | " # return non-null return value from the recursive calls\n",
28 | " left = dfs(root.left, target)\n",
29 | " if left is not None:\n",
30 | " return left\n",
31 | " # at this point, we know left is null, and right could be null or non-null\n",
32 | " # we return right child's recursive call result directly because\n",
33 | " # - if it's non-null we should return it\n",
34 | " # - if it's null, then both left and right are null, we want to return null\n",
35 | " return dfs(root.right, target) # ~ return dfs(root.left, target) or dfs(root.right, target)"
36 | ]
37 | },
38 | {
39 | "cell_type": "markdown",
40 | "metadata": {},
41 | "source": [
42 | "\n",
43 | "\n",
44 | "\n",
45 | "\n",
46 | "\n",
47 | "\n",
48 | ""
49 | ]
50 | },
51 | {
52 | "cell_type": "markdown",
53 | "metadata": {},
54 | "source": [
55 | "Being able to visualize recursion and call stack of a DFS function in your mind is extremely important. Please take a minute to make sure you internalize it and come back to this page any time you need to look at it.\n",
56 | "\n",
57 | "The example above also introduces two other concepts, backtracking and divide and conquer. The action of retracing steps (e.g. from 2 we first visited 3 depth first and retraced back and visit the other child 4) is called backtracking. Backtracking and DFS are similar concepts and essentially the same thing since in DFS you always \"backtrack\" after exploring a deeper node. It's like saying I program computers by doing coding. If we really want to make the distinction, then backtracking is the concept of retracing and DFS is the algorithm that implements it. In computer science textbooks, backtracking is often mentioned and associated with combinatorial search problems. We will do the same in the course.\n",
58 | "\n",
59 | "We have two recursive calls dfs(root.left) and dfs(root.right), and return based on results from the recursive calls. This is also a divide and conquer algorithm, i.e. splitting into subproblems of the same type (search in left and right children) until they are simple enough to be solved directly (null nodes or found target) and combine the results from these subproblems (return non-null node). We'll investigate divide and conquer more in a later module."
60 | ]
61 | },
62 | {
63 | "cell_type": "markdown",
64 | "metadata": {},
65 | "source": [
66 | "# When to use DFS\n",
67 | "\n",
68 | "## Tree\n",
69 | "DFS is essentially pre-order tree traversal.\n",
70 | "\n",
71 | "- Traverse and find/create/modify/delete node\n",
72 | "- Traverse with return value (finding max subtree, detect balanced tree)\n",
73 | "\n",
74 | "## Combinatorial problems\n",
75 | "DFS/backtracking and combinatorial problems are a match made in heaven (or silver bullet and werewolf 😅). As we will see in the Combinatorial Search module, combinatorial search problems boil down to searching in trees.\n",
76 | "\n",
77 | "- How many ways are there to arrange something\n",
78 | "- Find all possible combinations of ...\n",
79 | "- Find all solutions to a puzzle\n",
80 | "\n",
81 | "## Graph\n",
82 | "Trees are special graphs that have no cycle. We can still use DFS in graphs with cycles. We just have to record the nodes we have visited and avoiding re-visiting them and going into an infinite loop.\n",
83 | "\n",
84 | "- Find a path from point A to B\n",
85 | "- Find connected components\n",
86 | "- Detect cycles\n"
87 | ]
88 | },
89 | {
90 | "cell_type": "markdown",
91 | "metadata": {},
92 | "source": []
93 | }
94 | ],
95 | "metadata": {
96 | "kernelspec": {
97 | "display_name": "Python 3.10.4 64-bit",
98 | "language": "python",
99 | "name": "python3"
100 | },
101 | "language_info": {
102 | "codemirror_mode": {
103 | "name": "ipython",
104 | "version": 3
105 | },
106 | "file_extension": ".py",
107 | "mimetype": "text/x-python",
108 | "name": "python",
109 | "nbconvert_exporter": "python",
110 | "pygments_lexer": "ipython3",
111 | "version": "3.10.4"
112 | },
113 | "orig_nbformat": 4,
114 | "vscode": {
115 | "interpreter": {
116 | "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
117 | }
118 | }
119 | },
120 | "nbformat": 4,
121 | "nbformat_minor": 2
122 | }
123 |
--------------------------------------------------------------------------------
/dfs/04 dfs on tree.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# DFS on Tree 🌳🌵\n",
8 | "# 1. Think like ya are a Node! 🤓⚾\n",
9 | "\n",
10 | "From Node perspective, ya only know;\n",
11 | "\n",
12 | "1. Your value\n",
13 | "2. How to get to your childrean\n",
14 | "3. Recurssion will take care of other things!\n",
15 | "\n",
16 | "The key to solving tree problems using DFS is to think from the perspective of a node instead of looking at the whole tree. This is in line with how recursion is written. Reason from a node's perspective, decide how the current node should be proceeded, then recurse on children and let recursion takes care of the rest.\n",
17 | "\n",
18 | "When you are a node, the only things you know are 1) your value and 2) how to get to your children. So the recursive function you write manipulates these things.\n",
19 | "\n",
20 | "Given that, the template for DFS on tree is:\n"
21 | ]
22 | },
23 | {
24 | "cell_type": "code",
25 | "execution_count": null,
26 | "metadata": {},
27 | "outputs": [],
28 | "source": [
29 | "def dfs(node, state):\n",
30 | " if node is None:\n",
31 | " .....\n",
32 | " return .....\n",
33 | " \n",
34 | " left = dfs(node.left, state)\n",
35 | " right = dfs(node.right, state)\n",
36 | "\n",
37 | " .....\n",
38 | "\n",
39 | " return ......."
40 | ]
41 | },
42 | {
43 | "cell_type": "markdown",
44 | "metadata": {},
45 | "source": [
46 | "# 2. Defining the recursive function ⤴️ ⛳\n",
47 | "\n",
48 | "There are 2 things that we need to know to define the recursion;\n",
49 | "\n",
50 | "1. **Return value (passing value up from child to parent)** \n",
51 | "What do we want to return after visiting a node. For example, for the max depth problem this is the max depth for the current node's subtree. If we are looking for a node in the tree, we'd want to return that node if found, else return null. Use the return value to pass information from children to parent.\n",
52 | "\n",
53 | "2. **Identify state(s) (passing value down from parent to child)**\n",
54 | "What states do we need to maintain to compute the return value for the current node. For example, to know if the current node's value is larger than its parent we have to maintain the parent's value as a state. State becomes DFS's function arguments. Use states to pass information from parent to children.\n",
55 | "\n",
56 | "Consider the problem of pretty-print a binary tree. Given directory tree;\n",
57 | "\n",
58 | "\n",
59 | "\n",
60 | "We want to \"pretty-print\" the directory structure with indents like this:\n",
61 | "\n",
62 | "```\n",
63 | "/\n",
64 | " foo\n",
65 | " baz\n",
66 | " bar\n",
67 | " \n",
68 | "```\n",
69 | " \n",
70 | "If so, we can pass the current indent level as a state of the recursive call."
71 | ]
72 | },
73 | {
74 | "cell_type": "code",
75 | "execution_count": null,
76 | "metadata": {},
77 | "outputs": [],
78 | "source": [
79 | "indent_per_level = ' '\n",
80 | "def dfs(node, indent_level):\n",
81 | " ...\n",
82 | " current_indent_level = indent_level + indent_per_level\n",
83 | " print(current_indent_level + node.val)\n",
84 | " dfs(node, current_indent_level)"
85 | ]
86 | },
87 | {
88 | "cell_type": "markdown",
89 | "metadata": {},
90 | "source": [
91 | "# 3. Using return value vs. global variable Approches 🤓\n",
92 | "\n",
93 | "Consider the problem of finding Max value in a tree! To do so, we have 2 approaches;\n",
94 | "\n",
95 | "\n",
96 | "\n",
97 | "1. **Using return value (divide and conquer)**\n",
98 | "One way to solve it is to use return value to pass the maximum value we have encountered back to parent node, and let the parent node compare it with the return value from the other child. This is more of a divide and conquer approach."
99 | ]
100 | },
101 | {
102 | "cell_type": "code",
103 | "execution_count": null,
104 | "metadata": {},
105 | "outputs": [],
106 | "source": [
107 | "def find_max_dfs(node):\n",
108 | " if not node:\n",
109 | " return None\n",
110 | " left = find_max_dfs(node.left)\n",
111 | " right = find_max_dfs(node.right)\n",
112 | "\n",
113 | " return max(node.val , left, right)"
114 | ]
115 | },
116 | {
117 | "cell_type": "markdown",
118 | "metadata": {},
119 | "source": [
120 | "2. **Using global variable**\n",
121 | "Another way to solve this is to traverse the tree while keeping a global variable that keeps track of the maximum value we have encountered. After the dfs, we return the global variable."
122 | ]
123 | },
124 | {
125 | "cell_type": "code",
126 | "execution_count": null,
127 | "metadata": {},
128 | "outputs": [],
129 | "source": [
130 | "# global variable to record current max value\n",
131 | "# initialize to minimum value possible so any node will be larger\n",
132 | "def dfs_helper(node):\n",
133 | " if not node:\n",
134 | " return None\n",
135 | " max_val = 0\n",
136 | " if node.val > max_val: # update the global variable if current value is larger\n",
137 | " max_val = node.val\n",
138 | "\n",
139 | " dfs_helper(root.left)\n",
140 | " dfs_helper(root.right)\n",
141 | "\n",
142 | "\n",
143 | "def find_max(root):\n",
144 | " dfs_helper(root) # kick off dfs from root node\n",
145 | " return max_val\n"
146 | ]
147 | },
148 | {
149 | "cell_type": "markdown",
150 | "metadata": {},
151 | "source": [
152 | "It's more of a personal preference which one you use. One could argue global variables are bad and therefore the divide and conquer. However, sometimes it's easier to use a global variable. Recall that divide and conquer has two steps - partition and merge. If the merge step is complex, then using a global variable might simplify things."
153 | ]
154 | },
155 | {
156 | "cell_type": "markdown",
157 | "metadata": {},
158 | "source": [
159 | "# DFS Relavant Questions:\n",
160 | "\n",
161 | "## DFS on Tree:\n",
162 | "\n",
163 | "- [Max Depth BT](https://leetcode.com/problems/maximum-depth-of-binary-tree/)\n",
164 | "\n",
165 | "\n",
166 | "## DFS on BST:\n"
167 | ]
168 | },
169 | {
170 | "cell_type": "markdown",
171 | "metadata": {},
172 | "source": []
173 | },
174 | {
175 | "cell_type": "code",
176 | "execution_count": null,
177 | "metadata": {},
178 | "outputs": [],
179 | "source": []
180 | }
181 | ],
182 | "metadata": {
183 | "kernelspec": {
184 | "display_name": "Python 3.10.4 64-bit",
185 | "language": "python",
186 | "name": "python3"
187 | },
188 | "language_info": {
189 | "codemirror_mode": {
190 | "name": "ipython",
191 | "version": 3
192 | },
193 | "file_extension": ".py",
194 | "mimetype": "text/x-python",
195 | "name": "python",
196 | "nbconvert_exporter": "python",
197 | "pygments_lexer": "ipython3",
198 | "version": "3.10.4"
199 | },
200 | "orig_nbformat": 4,
201 | "vscode": {
202 | "interpreter": {
203 | "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
204 | }
205 | }
206 | },
207 | "nbformat": 4,
208 | "nbformat_minor": 2
209 | }
210 |
--------------------------------------------------------------------------------
/dfs/leetcode/balanced_tree.py:
--------------------------------------------------------------------------------
1 | # Question: Given a binary tree, determine if it is height-balanced.
2 | #NOTE: check for every n each node is balanced or not
3 |
4 | def balanced(root):
5 | def dfs(root):
6 | if not root:
7 | return [True,0] # [is_balanced, height]
8 |
9 | left, right = dfs(root.left), dfs(root.right)
10 | diff = abs(left[1] - right[1])
11 |
12 | balanced = True if (left[0] and right[0] and diff <= 1) else False
13 |
14 | return (balanced, max(left[1], right[1]) + 1) #`max(left[1], right[1]) + 1` is the height of the tree
15 | return dfs(root)[0]
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/dfs/leetcode/employee_inform_time.py:
--------------------------------------------------------------------------------
1 | import collections
2 |
3 |
4 | def informTime(n, headID, manager, informTime):
5 | subs = collections.defaultdict(list) # key: manager, value: list of employees
6 | for i, emp in enumerate(manager):
7 | subs[emp].append(i)
8 |
9 | stack = [[headID, 0]] # [manager, time]
10 | time = 0
11 |
12 | while stack: # while stack is not empty
13 | man, man_t = stack.pop()
14 | time = max(time, man_t) #
15 |
16 | for sub in subs[man]:
17 | stack.append([sub, man_t + informTime[man]])
18 |
19 | return time
--------------------------------------------------------------------------------
/dfs/leetcode/encode_str.py:
--------------------------------------------------------------------------------
1 | def ecode(strs): #O(n)
2 | res = ""
3 | for s in strs: #go through each string
4 | res += str(len(str)) + "#" + s
5 |
6 | return res
7 |
8 |
9 | def decode(strs):
10 | res, i = [], 0 #i is gonna tell us where we are in the string
11 | while i < len(strs): #iterate chr by chr
12 | j = i #we wanna find the end of the string ~ delimiter ~ where the word ends # j is the second pointer
13 | while str[j] != "#": #while we have chr left to iterate
14 | j += 1
15 |
16 | length = int(str[i:j]) #untill the point we reach end of the string #how far we gotta go inorder to get all of the chrs after j
17 | res.append(str[j + 1: j + 1 + length]) #get the word
18 | i = j + 1 + length #move up to the nxt word in the list ~ update i
19 |
20 | return res
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/dfs/leetcode/good_points.py:
--------------------------------------------------------------------------------
1 | #Question: return the number of good points in the tree; good points are points that are greater than or equal to the max value of the left subtree and the right subtree
2 | #DFS is used to traverse the tree
3 |
4 |
5 | def goodPoints(root):
6 |
7 | def dfs(node, maxVal):
8 | if not node:
9 | return 0
10 |
11 | count = 1 if node.val >= maxVal else 0
12 | maxVal = max(maxVal, node.val)
13 |
14 | count += dfs(node.left, maxVal)
15 | count += dfs(node.right, maxVal)
16 |
17 | return count
18 |
19 | return dfs(root,root.val)
--------------------------------------------------------------------------------
/dfs/leetcode/lca.py:
--------------------------------------------------------------------------------
1 | #Idea is to find the separation point between p and q. n each time compare the value of the node with p and q. Pay attention to property of a BST
2 |
3 | def LCA(root, p, q):
4 | if not root or not p or not q:
5 | return None
6 |
7 | elif root.val < p.val and root.val < q.val:
8 | return LCA(root.right, p, q)
9 |
10 | elif root.val > p.val and root.val > q.val:
11 | return LCA(root.left, p, q)
12 |
13 | else:
14 | return root
15 |
16 |
17 |
18 | def lca(root, p, q):
19 | def dfs(root):
20 | if not root:
21 | return None
22 | if q.val < root.val and p.val< root.val:
23 | return dfs(root.left)
24 |
25 | elif p.val > root.val and q.val > root.val:
26 | return dfs(root.right)
27 | else: return root
28 |
29 | return dfs(root)
--------------------------------------------------------------------------------
/dfs/leetcode/longest_consecutive_path.py:
--------------------------------------------------------------------------------
1 | def longest_consecutive_path(root):
2 | if not root:
3 | return 1
--------------------------------------------------------------------------------
/dfs/leetcode/max_depth.py:
--------------------------------------------------------------------------------
1 | # MaxDepth
2 | # Question 1: Given a binary tree, find its maximum depth.
3 | # note to go for return val approach of dfs, what do ya wanna return from children to parent after visiting them?
4 | def maxDepth(root):
5 | if not root:
6 | return 0
7 | return max(maxDepth(root.left), maxDepth(root.right)) + 1 # +1 for the root
--------------------------------------------------------------------------------
/dfs/leetcode/n-array-tree_depth.py:
--------------------------------------------------------------------------------
1 | #Question: maximum depth on n-array tree
2 |
3 | def maxDepth(root):
4 | if not root:
5 | return 0
6 | if root.children:
7 | return max(maxDepth(child)+1 for child in root.children)
8 | return 1
9 |
10 |
--------------------------------------------------------------------------------
/dfs/leetcode/readme.md:
--------------------------------------------------------------------------------
1 | # Please refer to [this](https://docs.google.com/spreadsheets/d/1olmMtN1vGS0BG9WNqPeCB8DWjJqezOBQp5ERbtZHiTk/edit#gid=1492593591) chart for following the similar questions and notes for solving them!
--------------------------------------------------------------------------------
/dfs/leetcode/serialize_tree.py:
--------------------------------------------------------------------------------
1 | class TreeNode:
2 | def __init__(self, val):
3 | self.val = val
4 | self.left = None
5 | self.right = None
6 |
7 | class CodeC:
8 |
9 | def serialize(root): #preorder traversal
10 | res = []
11 | def dfs(node):
12 | if not node:
13 | res.append("None") # serialize None as "None"
14 | return
15 |
16 | res.append(str(node.val)) # serialize the node
17 |
18 | dfs(node.left)
19 | dfs(node.right)
20 |
21 | dfs(root)
22 | return ", ".join(res)
23 |
24 |
25 | def deserialize(self, data):
26 | vals = data.split(",")
27 | self.i = 0
28 |
29 | def dfs():
30 | if vals[self.i] == "Null":
31 | self.i += 1
32 | return None
33 | node = TreeNode(int(vals[self.i]))
34 | self.i += 1
35 | node.left = dfs()
36 | node.right = dfs()
37 | return node
38 | return dfs()
39 |
--------------------------------------------------------------------------------
/dfs/leetcode/smallest_region.py:
--------------------------------------------------------------------------------
1 | def smallestRegion(regions, reg1, reg2):
2 | dic = {}
3 | for region in regions:
4 | parent = region[0]
5 | for i in range(1,len(region)):
6 | dic[region[i]] = parent
7 |
8 | dic_region1 = {}
9 | while region1 in dic:
10 | dic_region1[region1] = 1
11 | region1 = dic[region1]
12 |
13 | while region2 in dic:
14 | if region2 in dic_region1:
15 | return region2
16 | region2 = dic[region2]
17 | return regions[0][0]
18 |
--------------------------------------------------------------------------------
/dfs/leetcode/validate_BST.py:
--------------------------------------------------------------------------------
1 | def validateBST(root):
2 | def valid(node, left, right):
3 | if not node:
4 | return True
5 | if not (node.val > left and node.val < right):
6 | return False
7 |
8 | return (valid(node.left, left, node.val) and
9 | valid(node.right, node.val, right))
10 |
11 | return valid(root, float('-inf'), float('inf'))
--------------------------------------------------------------------------------
/heap/01 Heap fundamental.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Priority Queue and Heap 🗻\n",
8 | "## What is the relationship between priority queue and heap?\n",
9 | "Priority Queue is an Abstract Data Type, and Heap is the concrete data structure we use to implement a priority queue.\n",
10 | "\n",
11 | "# Priority Queue\n",
12 | "A priority queue is a data structure that consists of a collection of items and supports the following operations:\n",
13 | "\n",
14 | "- insert: insert an item with a key\n",
15 | "- delete_min/delete_max: remove the item with the smallest/largest key and returning it.\n",
16 | "\n",
17 | "## Note that;\n",
18 | "\n",
19 | "- we only allow getting and deleting the element with min/max key and NOT arbitrary key.\n",
20 | "\n",
21 | "## Use cases;\n",
22 | "The hospital triage process is a quintessential priority queue. Patients are sorted based on the severity of their condition. For example, a person with the cold comes in and he is placed near the end of the queue. Then a person who had a car accident comes in, and he is placed before the person with the cold even though he came in later because his condition is more severe. Severity is the key in this case.\n",
23 | "\n",
24 | "Consider a problem like Merge K sorted lists. We need to keep track of min value among k elements (smallest element in each sorted list) and remove the min value and insert new values at will while still having access to the min value at any point in time. Here are some other problems where the priority queue comes in handy."
25 | ]
26 | },
27 | {
28 | "cell_type": "markdown",
29 | "metadata": {},
30 | "source": [
31 | "- k closest pointers to origin\n",
32 | "- kth largest element\n",
33 | "- kth largest element in a stream\n",
34 | "- Median of a data stream\n",
35 | "- Uniform Cost Search"
36 | ]
37 | },
38 | {
39 | "cell_type": "markdown",
40 | "metadata": {},
41 | "source": [
42 | "# Implement Priority Queue using an array\n",
43 | "\n",
44 | "To do this, we could try using\n",
45 | "\n",
46 | "an unsorted array, insert would be O(1) as we just have to put it at the end, but finding and deleting min value would be O(N) since we have to loop through the entire array to find it\n",
47 | "a sorted array, finding min value would be easy O(1), but it would be O(N) to insert since we have to loop through to find the correct position of the value and move elements after the position to make space and insert into the space\n",
48 | "There must be a better way! \"Patience you must have, my young padawan.\" Enter Heap."
49 | ]
50 | },
51 | {
52 | "cell_type": "markdown",
53 | "metadata": {},
54 | "source": [
55 | "# Heap\n",
56 | "## Max Heap and Min Heap\n",
57 | "There are two kinds of heaps - Min Heap and Max Heap. A Min Heap is a tree that has two properties:\n",
58 | "\n",
59 | "1. almost complete, i.e. every level is filled except possibly the last(deepest) level. The filled items in the last level are left-justified.\n",
60 | "2. for any node, its key (priority) is greater than its parent's key (Min Heap).\n",
61 | "\n",
62 | "A Max Heap has the same property #1 and opposite property #2, i.e. for any node, its key is less than its parent's key."
63 | ]
64 | },
65 | {
66 | "cell_type": "markdown",
67 | "metadata": {},
68 | "source": []
69 | }
70 | ],
71 | "metadata": {
72 | "kernelspec": {
73 | "display_name": "Python 3.10.4 64-bit",
74 | "language": "python",
75 | "name": "python3"
76 | },
77 | "language_info": {
78 | "name": "python",
79 | "version": "3.10.4"
80 | },
81 | "orig_nbformat": 4,
82 | "vscode": {
83 | "interpreter": {
84 | "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
85 | }
86 | }
87 | },
88 | "nbformat": 4,
89 | "nbformat_minor": 2
90 | }
91 |
--------------------------------------------------------------------------------
/heap/02 heap definition.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# What is Binary Heap? 🌵🗻\n",
8 | "\n",
9 | "A Binary Heap is a **Binary Tree** with following properties;\n",
10 | "\n",
11 | "1) When we say its a Binary Tree, then it means that each and every Parent Node, could possibly have,**AT MOST**, two children!\n",
12 | "\n",
13 | "2) Must be a complete tree! This means that **All** levels are completely filled from left to right, expet the very last level that is not needed to be full! Look at the picture below;\n",
14 | "\n",
15 | "\n",
16 | "\n",
17 | "3) A Binary Heap is either Min Heap or Max Heap. In a Min Binary Heap, the value value at **root** must be minimum among **all values** present in Binary Heap. The same property must be recursively true for all nodes in Binary Tree. Max Binary Heap is essencially the same, but the root node is the Maximum value among all the present nodes! Below is an example of Binary Min Heap. \n",
18 | "\n",
19 | ""
20 | ]
21 | },
22 | {
23 | "cell_type": "markdown",
24 | "metadata": {},
25 | "source": [
26 | "# How is Binary Heap represented in Memory? 🛠️⚙️ \n",
27 | "\n",
28 | "Ya remember in the former section I mentioned, Binary Heap is a **Complete** Binary Tree? The **Complete** Tree nature of it, makes it ideal candidate for **Array** or **List** data structure to be implemented by!\n",
29 | "\n",
30 | "## Why Array is used for building?\n",
31 | "\n",
32 | "This is because in the case of say fixed-size array, we want maximum number of cells to be filled, right? (Because it occupies memory, so we don't want array cell to be empty)\n",
33 | "Now, since it is a Complete Tree(i.e. all levels except the very last level, are filled!) si it ensures that most of the cells are filled, so there would be no gap between the nodes, right? \n",
34 | "\n",
35 | "Then once we get that array, then with a simple eqution (below) we can get to the left and right child, which are basically the next or nearby cells in the array case, right? \n",
36 | "\n",
37 | "\n",
38 | "\n",
39 | "Note that The **root** element will be at Arr[0]"
40 | ]
41 | },
42 | {
43 | "cell_type": "code",
44 | "execution_count": null,
45 | "metadata": {},
46 | "outputs": [],
47 | "source": [
48 | "class MinHeap:\n",
49 | " def __init__(self, capacity):\n",
50 | " self.customArr = capacity * [None] #create/initiate a custom arr or list\n",
51 | " self.capacity = capacity \n",
52 | " self.size = 0 #number of element currently in our heap\n",
53 | "\n",
54 | " def getindexParent(self,i): #getting the Parent Node index via the formula above \n",
55 | " return (i-1)//2\n",
56 | "\n",
57 | " def getindexLChild(self,i): #getting the Left Child index via the formula above \n",
58 | " return (2*i +1)\n",
59 | "\n",
60 | " def getindexRChild(self,i): #getting the Right child indes via the formula above\n",
61 | " return (2*i +2)\n",
62 | "\n",
63 | " def hasParent(index): #to have parent, the Array/List size gotta be bigger than Zero\n",
64 | " if index > 0:\n",
65 | " getindexParent(index)\n",
66 | "\n",
67 | " def hasLeftChild(index): #to have child, the node gotta be in range of the list/Arr, meaning it gotta be less than the size of the Arr/List\n",
68 | " if index < size:\n",
69 | " getindexLChild(index)\n",
70 | "\n",
71 | " def hasRightChild(index): #to have child, the node gotta be in range of the list/Arr, meaning it gotta be less than the size of the Arr/List\n",
72 | " if index < size:\n",
73 | " getindexRChild(index)\n",
74 | "\n",
75 | " def parentValue(index): #get the value of the Parent Node\n",
76 | " return index[getindexParent(index)]\n",
77 | "\n",
78 | " def leftchildValue(index): #get the value of the Left Node\n",
79 | " return index[getindexLChild(index)]\n",
80 | "\n",
81 | " def rightchildValue(index): #get the value of the Right Node\n",
82 | " return index[getindexRChild(index)]"
83 | ]
84 | }
85 | ],
86 | "metadata": {
87 | "language_info": {
88 | "name": "python"
89 | },
90 | "orig_nbformat": 4
91 | },
92 | "nbformat": 4,
93 | "nbformat_minor": 2
94 | }
95 |
--------------------------------------------------------------------------------
/heap/03 heap operations.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Main Operations on Heap ➕✂️\n",
8 | "\n",
9 | "In this section, we delve into two main operations on heap; **Insertion**, and **Deletion**\n",
10 | "\n",
11 | "# Insertion ➕\n",
12 | "\n",
13 | "The very first note that ya gotta know in insertion is that, we almost, **ALWAYS** insert an element at the very first right spot in the last layer, following from **Left** to **Right**! Okay? \n",
14 | "Now, look at the example below which is a **Min Heap**; \n",
15 | "\n",
16 | "\n",
17 | "\n",
18 | "Imagine we wanna insert node 8 into the heap, but **OOPS 🤦♀️😕** this is a Min-Heap, ya remember in the Min-heap case, all the children values gotta be smaller than the parents nodes? So, to solve this issue, we need to Swap the nodes and walk up untill we reach to the place in correct order fashion! "
19 | ]
20 | },
21 | {
22 | "cell_type": "code",
23 | "execution_count": null,
24 | "metadata": {},
25 | "outputs": [],
26 | "source": [
27 | "#Insertion in Heap\n",
28 | "def insert(item):\n",
29 | " item[size] = item \n",
30 | " size += 1\n",
31 | " heapifyUp()\n",
32 | "\n",
33 | "def swap(index1, index2):\n",
34 | " tmp = customArr[index1]\n",
35 | " customArr[index1] = customArr[index2]\n",
36 | " customArr[index2] = tmp\n",
37 | "\n",
38 | "def heapifyUp(index):\n",
39 | " item = size -1 #we wanna insert very left empty spot at the last level element in the heap\n",
40 | " while hasParent(index) and Parent(index) > item[index]: #walk up as long as there is a parent and is not in right fasion order\n",
41 | " swap(getindexParent(index), index)\n",
42 | " index= getindexParent(index)\n",
43 | "\n",
44 | " "
45 | ]
46 | }
47 | ],
48 | "metadata": {
49 | "language_info": {
50 | "name": "python"
51 | },
52 | "orig_nbformat": 4
53 | },
54 | "nbformat": 4,
55 | "nbformat_minor": 2
56 | }
57 |
--------------------------------------------------------------------------------
/heap/imgs/heap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/heap/imgs/heap.png
--------------------------------------------------------------------------------
/heap/imgs/insert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/heap/imgs/insert.png
--------------------------------------------------------------------------------
/heap/imgs/minBH.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/heap/imgs/minBH.png
--------------------------------------------------------------------------------
/heap/imgs/minBHrep.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/heap/imgs/minBHrep.png
--------------------------------------------------------------------------------
/heap/leetcode/moving best/re_organize_str.py:
--------------------------------------------------------------------------------
1 | import collections
2 | import heapq
3 |
4 | def reOrg(strs):
5 | count = collections.Counter(strs)
6 | max_heap = [[-cnt, char] for char, cnt in count.items()]
7 | heapq.heapify(max_heap)
8 |
9 | prev = None
10 | res= ""
11 |
12 | while max_heap or prev:
13 | if prev and not max_heap:
14 | return ""
15 | cnt, char = heapq.heappop(max_heap)
16 | res += char
17 | cnt += 1
18 |
19 | if prev:
20 | heapq.heappush(max_heap, prev)
21 | prev = None
22 |
23 | if cnt != 0:
24 | prev = [cnt, char]
25 |
26 | return res
27 |
28 |
--------------------------------------------------------------------------------
/heap/leetcode/moving best/task_scheduler.py:
--------------------------------------------------------------------------------
1 | import collections
2 | import heapq
3 |
4 | def taskscheduler(tasks, n):
5 | count = collections.Counter(tasks) #to count the most-least frequent tasks
6 | max_heap = [-cnt for cnt in count.values()] #to create a max heap
7 | heapq.heapify(max_heap) #to create a max heap
8 |
9 | time = 0
10 | q = collections.deque() #[conut/task, time to procceed]
11 | while max_heap or q:
12 | time += 1
13 |
14 | if not max_heap:
15 | time = q[0][1]
16 |
17 | else:
18 | cnt = 1 + heapq.heappop(max_heap)
19 | if cnt:
20 | q.append([cnt, time +n])
21 |
22 | if q and q[0][1] == time: #when return back to the task to procceed forward with it
23 | heapq.heappush(max_heap, q.popleft()[0])
24 |
25 | return time
26 |
--------------------------------------------------------------------------------
/heap/leetcode/moving best/ugly_num.py:
--------------------------------------------------------------------------------
1 | def ugly_num(n):
2 |
3 | if n <= 0:
4 | return False
5 |
6 | for x in [2,3,5]:
7 | while n % x ==0:
8 | n = n // x
9 | return n == 1
--------------------------------------------------------------------------------
/heap/leetcode/readme.md:
--------------------------------------------------------------------------------
1 | # Please refer to [this](https://docs.google.com/spreadsheets/d/1olmMtN1vGS0BG9WNqPeCB8DWjJqezOBQp5ERbtZHiTk/edit?pli=1#gid=1492593591) chart to see the relavant questions.
--------------------------------------------------------------------------------
/heap/leetcode/topk/char_by_freq.py:
--------------------------------------------------------------------------------
1 | from collections import Counter
2 | import heapq
3 |
4 | def sort_char_by_freq(s):
5 | count = Counter(s)
6 | max_heap= []
7 | heapq.heapify(max_heap)
8 |
9 | for ch, freq in count.items():
10 | heapq.heappush(max_heap, [-freq, ch])
11 |
12 | res = ""
13 | while(len(max_heap)):
14 | freq, letter = heapq.heappop(max_heap)
15 | print(freq,letter)
16 |
17 | res += (letter * (-freq))
18 | print(res)
19 |
20 | return res
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | if __name__ == "__main__":
29 | print(sort_char_by_freq("loovXxvvve"))
--------------------------------------------------------------------------------
/heap/leetcode/topk/k_closest_toOrigin.py:
--------------------------------------------------------------------------------
1 | import heapq
2 |
3 | def closestPoint(points, k):
4 | pts = [] #list of the points
5 | for x, y in points: #for each point in the list of points
6 | dist = (abs(x)**2) + (abs(y)**2)
7 | pts.append([dist, x, y]) #add the distance and the point to the list
8 |
9 | res = []
10 | heapq.heapify(pts) #convert the list to a heap
11 | for _ in range(k):
12 | dist, x, y = heapq.heappop(pts) #pop the top k points
13 | res.append([x,y]) #add the popped point to the list
14 | return res
15 |
--------------------------------------------------------------------------------
/heap/leetcode/topk/k_freq_words.py:
--------------------------------------------------------------------------------
1 | from collections import Counter
2 | import heapq
3 | # Approach 0
4 | class Solution(object):
5 | def topKFrequent(self, words, k):
6 | """
7 | :type words: List[str]
8 | :type k: int
9 | :rtype: List[str]
10 | """
11 | count = Counter(words) #{word:freq}
12 | maxHeap = []
13 | heapq.heapify(maxHeap)
14 | for word, freq in count.items():
15 | heapq.heappush(maxHeap, [-freq,word])
16 |
17 | res = []
18 | for i in range(k):
19 | out = heapq.heappop(maxHeap)
20 | res.append(out[1])
21 | return res
22 |
23 | #Aprroach 1
24 | def topKFrequent(words, k):
25 | counts = collections.Counter(words) #build a dictionary of words and their frequencies
26 |
27 | arr_to_be_converted = [(-count, word) for word, count in counts.items()] #convert the dictionary to a list of tuples
28 |
29 | heapq.heapify(arr_to_be_converted) #convert the arr to a heap
30 |
31 | return [heapq.heappop(arr_to_be_converted)[1] for _ in range(k)] #return the top k words
32 |
33 |
34 | #Approach 2
35 | def topKFrequent(words, k):
36 | word_freq = {}
37 | for word in words:
38 | if word not in word_freq:
39 | word_freq[word] = 1
40 |
41 | else:
42 | word_freq[word] += 1
43 |
44 | min_heap = []
45 |
46 | for word, freq in word_freq.items():
47 | heapq.heappush(min_heap, (freq, word))
48 |
49 | if len(min_heap) > k:
50 | heapq.heappop(min_heap)
51 |
52 | res = []
53 | while min_heap:
54 | w = heapq.heappop(min_heap)
55 | res.append(w[1])
56 |
57 | return res[::-1]
58 |
--------------------------------------------------------------------------------
/heap/leetcode/topk/k_largest_array.py:
--------------------------------------------------------------------------------
1 | import heapq
2 | def largest(nums, k):
3 | heap = []
4 | for num in nums:
5 | heapq.heappush(heap, (-1 * num)) # -1 * num to make it a max heap
6 |
7 | largest = 0 # largest is the largest element in the heap
8 | for _ in range(k):
9 | largest = -1 * heapq.heappop(heap)
10 | return largest
--------------------------------------------------------------------------------
/heap/leetcode/topk/largest_word_count_msg.py:
--------------------------------------------------------------------------------
1 | from matplotlib import collections
2 |
3 | #Approach 1
4 | def largestWordCount(messages, senders):
5 | d = {}
6 | res = []
7 |
8 | for i in range(len(messages)):
9 | if senders[i] not in d:
10 | d[senders[i]] = len(messages[i].split())
11 |
12 | else:
13 | d[senders[i]] += len(messages[i].split())
14 | x = max(d.values())
15 |
16 | for k, v in d.items():
17 | if v == x:
18 | res.append(k)
19 |
20 | if len(res) == 1:
21 | return res[0]
22 |
23 | else:
24 | res = sorted(res)[::-1]
25 | return res[0]
26 |
27 |
28 |
29 |
30 | #Approach 2
31 | def largestWordCount(messages, senders):
32 |
33 | counts = collections.Counter()
34 | for m, s in zip(messages, senders):
35 | counts[s] += m.count(' ') + 1
36 |
37 | largestCount, largestSender = 0, ""
38 | for sender, count in counts.items():
39 | if count > largestCount or (count == largestCount and sender > largestSender):
40 | largestCount, largestSender = count, sender
41 |
42 | return largestSender
43 |
--------------------------------------------------------------------------------
/img/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/1.png
--------------------------------------------------------------------------------
/img/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/2.png
--------------------------------------------------------------------------------
/img/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/3.png
--------------------------------------------------------------------------------
/img/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/4.png
--------------------------------------------------------------------------------
/img/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/5.png
--------------------------------------------------------------------------------
/img/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/6.png
--------------------------------------------------------------------------------
/img/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/7.png
--------------------------------------------------------------------------------
/img/Binary_Search.svg:
--------------------------------------------------------------------------------
1 |
78 |
--------------------------------------------------------------------------------
/img/amz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/amz.png
--------------------------------------------------------------------------------
/img/binary_search.001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/binary_search.001.png
--------------------------------------------------------------------------------
/img/dfs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/dfs.png
--------------------------------------------------------------------------------
/img/dfs_on_trees_intro_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/dfs_on_trees_intro_1.png
--------------------------------------------------------------------------------
/img/dfs_on_trees_intro_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/dfs_on_trees_intro_2.png
--------------------------------------------------------------------------------
/img/fb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/fb.png
--------------------------------------------------------------------------------
/img/goo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/goo.png
--------------------------------------------------------------------------------
/img/inorder_traversal.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/inorder_traversal.gif
--------------------------------------------------------------------------------
/img/postorder_traversal.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/postorder_traversal.gif
--------------------------------------------------------------------------------
/img/preorder_traversal.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/preorder_traversal.gif
--------------------------------------------------------------------------------
/img/r.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/r.png
--------------------------------------------------------------------------------
/img/stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/stack.png
--------------------------------------------------------------------------------
/img/stats.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/stats.png
--------------------------------------------------------------------------------
/img/tree_intro.001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shirinyamani/algo-monster/71c0722b1d8ba54bce7088a37abd9b4521d75ddf/img/tree_intro.001.png
--------------------------------------------------------------------------------
/stack/01 intro.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# What is Stack?\n",
8 | "So what is a stack, and how does it work? Well, just like a linked list, a stack is nothing more than a data structure that contains a whole bunch of elements. And, like linked lists, stacks are linear, which means that there is a sequence and an order to how they are constructed and traversed!\n",
9 | "\n",
10 | "The interesting thing about how stacks work, is that they only really have one direction to them. That is to say, if we wanted to add elements to this structure, or if we wanted to remove elements from it, we’d have to do it starting from one place: the top of the stack.\n",
11 | "\n",
12 | "We can think of it like a stack data type just like a stack of books. If we had a stack of fifty books, we’d have to add the fifty-first book to the top of our stack, right? We probably wouldn’t be trying to stick it at the bottom of the pile of books. That’s the same concept that applies to stack structures: the insertion and deletion of elements can only happen from one end of the stack — the top.\n",
13 | "\n",
14 | "This behavior of inserting and deleting from the same location has a name, too — and sometimes, this name is used to refer to stacks as well: last in, first out.\n",
15 | "\n",
16 | "The last in, first out principle (LIFO) refers to the rule that whichever element is the last one to go into the stack will be the very first one that has to go out.\n",
17 | "\n",
18 | "Conversely, this also means that the first element that you put into the stack will always end up being the last one that goes out!\n",
19 | "\n",
20 | "Last in First out structure, imagine ya wanna implement sth like MS word, so ya wanna undo what ya just written or maybe the word before that, ya wanna edit that and so on! \n",
21 | "- e.g. usage: crl Z on keyboard, website history, ya wanna traverse that history in a reverse order!\n",
22 | "- Like a real-world stack of plates\n",
23 | "- We can only interact with the very last item of the stack which is the top element of it!\n",
24 | "\n",
25 | "\n",
26 | "\n",
27 | "# Why do we need stacks?\n",
28 | "Whenever we have a functionality in our program that utilizes the last data, first!\n",
29 | "\n"
30 | ]
31 | },
32 | {
33 | "cell_type": "markdown",
34 | "metadata": {},
35 | "source": [
36 | "# Push and Pop from/into Stack?\n",
37 | "- Happens in **reverse** order, meaning the last item in, would be the first item out or being removed from the stack!\n",
38 | "- Becareful with the pop method, ya cannot pop from an empty list, right? So thats why ya gotta have the condition of \"while stack...\""
39 | ]
40 | }
41 | ],
42 | "metadata": {
43 | "kernelspec": {
44 | "display_name": "Python 3.10.4 64-bit",
45 | "language": "python",
46 | "name": "python3"
47 | },
48 | "language_info": {
49 | "name": "python",
50 | "version": "3.10.4"
51 | },
52 | "orig_nbformat": 4,
53 | "vscode": {
54 | "interpreter": {
55 | "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
56 | }
57 | }
58 | },
59 | "nbformat": 4,
60 | "nbformat_minor": 2
61 | }
62 |
--------------------------------------------------------------------------------
/stack/queue.py:
--------------------------------------------------------------------------------
1 | from collections import deque
2 | def bfs(root):
3 | queue = deque([root])
4 | while len(queue) > 0:
5 | node = queue.popleft()
6 | if node.left:
7 | queue.append(node.left)
8 | if node.right:
9 | queue.append(node.right)
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | if __name__ == "__main__":
23 | bfs([1,2,3,4,5,6,7])
--------------------------------------------------------------------------------