├── .gitignore ├── 1. Basics ├── 1. Count Digits │ ├── brute-force.py │ └── optimal.py ├── 2. Reverse a number │ ├── leetcode-solution.py │ └── optimal.py ├── 3. Print divisors │ ├── brute-force.py │ ├── optimal.py │ └── question.py ├── 4. Check prime │ ├── brute-force.py │ ├── optimal.py │ └── question.py ├── 5. Check Palindrome │ └── optimal.py └── 6. Sum of all divisors │ ├── better.py │ ├── brute-force.py │ └── optimal.py ├── 10. Sliding Window └── Medium │ ├── 1. Leetcode 3 - Longest Substring Without Repeating Characters │ ├── brute-force.py │ └── optimal.py │ ├── 2. Leetcode 1004 - Max Consecutive Ones III │ ├── better.py │ ├── brute-force.py │ └── optimal.py │ ├── 3. Fruits and Baskets │ ├── better.py │ ├── brute-force.py │ └── optimal.py │ ├── 4. Leetcode 424 - Longest Repeating Character Replacement │ ├── better.py │ ├── brute-force.py │ └── optimal.py │ ├── 5. Leetcode 930 - Binary Subarrays With Sum │ ├── brute-force.py │ └── optimal.py │ ├── 6. Leetcode 1248 - Count Number of Nice Subarrays │ └── optimal.py │ ├── 7. Leetcode 1358 - Number of Substrings Containing All Three Characters │ ├── better.py │ ├── brute-force.py │ └── optimal.py │ └── 8. Leetcode 1423 - Maximum Points You Can Obtain from Cards │ └── optimal.py ├── 11. Stack and Queues ├── 1. stack_using_arrays.py ├── 2. queue_using_array.py ├── 3. stack_using_single_queue.py ├── 4. queue_using_stack.py ├── 5. stack_using_LL.py ├── 6. queue_using_LL.py ├── deque_module.py ├── postfix,infix,prefxi conversion │ ├── 1. infix-to-postfix.py │ ├── 2. infix-to-prefix.py │ ├── 3. postfix-to-infix.py │ ├── 4. prefix-to-infix.py │ ├── 5. postfix-to-prefix.py │ └── 6. prefix-to-postfix.py └── questions │ ├── 1. Leetcode 225 - Implement Stack using Queues │ └── optimal.py │ ├── 2. Leetcode 232 - Implement Queue using Stacks │ └── optimal.py │ ├── 3. Leetcode 20 - Valid Parentheses │ └── optimal.py │ ├── 4. Leetcode 155 - Min Stack │ └── optimal.py │ ├── 5. Leetcode 496 - Next Greater Element I │ └── optimal.py │ ├── 6. Leetcode 503 - Next Greater Element II │ └── optimal.py │ ├── 7. Leetcode 146 - LRU Cache │ └── optimal.py │ ├── 7. Leetcode 42 - Trapping Rain Water │ ├── brute-force.py │ └── optimal.py │ └── 8. Leetcode 735 - Asteroid Collision │ └── optimal.py ├── 12. Recursion └── generate_non_cont_sequences.py ├── 13. Advance Recursion ├── Medium │ ├── 1. Leetcode 78 - Subsets │ │ └── code.py │ ├── 10. Leetcode 17 - Letter Combinations of a Phone Number │ │ └── optimal.py │ ├── 11. Leetcode 79 - Word Search │ │ └── optimal.py │ ├── 2. Leetcode 39 - Combination Sum │ │ └── code.py │ ├── 3. Leetcode 40 - Combination Sum II │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 4. Subset Sum │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 5. Leetcode 90 - Subsets II │ │ └── optimal.py │ ├── 6. Leetcode 131 - Palindrome Partitioning │ │ └── code.py │ ├── 7. Leetcode 51 - N-Queens │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 8. Rat in a Maze │ │ ├── brute-force.py │ │ └── optimal.py │ └── 9. Combination Sum 3 │ │ └── optimal.py ├── count_subsequences (all).py ├── print_subsequences (all).py └── print_subsequences (one).py ├── 14. Binary Trees ├── 1. Traversals │ ├── 1. dfs.py │ ├── 2. bfs.py │ ├── 3. Iterative Pre Order.py │ ├── 4. Iterative Inorder.py │ ├── 5. Iterative PostOrder (Using 2 stacks).py │ └── 6. Iterative PostOrder (Using 1 stack).py └── 2. Easy and Medium │ ├── 1. Leetcode 104 - Maximum Depth of Binary Tree │ ├── optimal [Iterative].py │ └── optimal [Recursive].py │ ├── 10. Leetcode 101 - Symmetric Tree │ └── optimal.py │ ├── 2. Leetcode 110 - Balanced Binary Tree │ ├── brute-force.py │ └── optimal.py │ ├── 3. Leetcode 543 - Diameter of Binary Tree │ └── optimal.py │ ├── 4. Leetcode 124 - Binary Tree Maximum Path Sum │ └── optimal.py │ ├── 5. Leetcode 100 - Same Tree │ └── optimal.py │ ├── 6. Leetcode 103 - Binary Tree Zigzag Level Order Traversal │ └── optimal.py │ ├── 7. Top View of Binary Tree │ └── optimal.py │ ├── 8. Bottom View of Binary Tree │ └── optimal.py │ └── 9. Leetcode 199 - Binary Tree Right Side View │ ├── brute-force.py │ └── optimal.py ├── 15. Dynamic Programming ├── 1. Introduction │ ├── 1. fibonacci [memo].py │ ├── 2. fibonacci [tabulation].py │ └── 3. fibonacci [optimized].py ├── 2. 1D-DP │ ├── 1. Leetcode 70 - Climbing Stairs │ │ ├── 1. tabulation.py │ │ └── 2. tabulation [optimized].py │ ├── 2. Frog Jump │ │ ├── 1. recursion.py │ │ ├── 2. memoization.py │ │ ├── 3. tabulation.py │ │ └── 4. tabulation [space optimized].py │ ├── 3. Minimal Cost │ │ ├── 1. memoization.py │ │ └── 2. tabulation.py │ ├── 4. Leetcode 198 - House Robber │ │ ├── 1. recursion.py │ │ ├── 2. memoization.py │ │ ├── 3. tabulation.py │ │ └── 4. tabulation [space optimize].py │ └── 5. Leetcode - 213 House Robber II │ │ └── tabulation [space optimized].py ├── 3. DP on 2D array │ ├── 1. Geek's Training │ │ ├── 1. recursion.py │ │ ├── 2. memoization.py │ │ ├── 3. tabulation.py │ │ └── 4. tabulation [space optimized].py │ └── 2. Leetcode 62 - Unique Paths │ │ ├── 1. recursion.py │ │ ├── 2. memoization.py │ │ ├── 3. tabulation.py │ │ └── 4. tabulation [space-optmize].py ├── 4. DP on Subsequence or Subarray │ └── 1. Subset sum equal to target │ │ ├── 1. recursion.py │ │ ├── 2. memoization.py │ │ ├── 3. tabulation.py │ │ └── 4. tabulation [space optimized].py ├── 5. DP on Stocks │ ├── 1. Leetcode 121 - Best Time to Buy and Sell Stock │ │ └── optimal.py │ ├── 2. Leetcode 122 - Best Time to Buy and Sell Stock II │ │ ├── 1. recursion.py │ │ ├── 2. memoization.py │ │ ├── 3. tabulation.py │ │ └── 4. tabulation [space-optimize].py │ ├── 3. Leetcode 123 - Best Time to Buy and Sell Stock III │ │ ├── 1. recursion.py │ │ ├── 2. memoization.py │ │ ├── 3. tabulation.py │ │ └── 4. tabulation [space optimize].py │ ├── 4. Leetcode 188 - Best Time to Buy and Sell Stock IV │ │ ├── 1. recursion.py │ │ ├── 2. memoization.py │ │ ├── 3. tabulation.py │ │ └── 4. tabulation [space-optimized].py │ ├── 5. Leetcode 309 - Best Time to Buy and Sell Stock with Cooldown │ │ ├── 1. recursion.py │ │ ├── 2. memoization.py │ │ ├── 3. tabulation.py │ │ └── 4. tabulation [space-optimize].py │ └── 6. Leetcode 714 - Best Time to Buy and Sell Stock with Transaction Fee │ │ ├── 1. recursion.py │ │ ├── 2. memoization.py │ │ ├── 3. tabulation.py │ │ └── 4. tabulation [space-optimize].py └── 6. DP on Strings │ ├── 1. Leetcode 1143 - Longest Common Subsequence │ ├── 1. recursion.py │ ├── 2. memoization.py │ └── 3. tabulation.py │ ├── 2. Print longest Subsequence │ └── optimal.py │ ├── 3. Longest Common Substring │ └── tabulation.py │ ├── 4. Leetcode 516 - Longest Palindromic Subsequence │ ├── 1. recursion.py │ ├── 2. memoization.py │ └── 3. tabulation.py │ └── 5. Leetcode 1092 - Shortest Common Supersequence │ └── optimal.py ├── 16. Graphs ├── 1. Introduction │ ├── adjacent-matrix.py │ └── listss.py ├── 2. Traversal │ ├── 1. bfs.py │ └── 2. dfs.py ├── 3. Questions │ ├── 1. Leetcode 547 - Number of Provinces │ │ ├── sol1.py │ │ └── sol2.py │ ├── 2. Leetcode 994 - Rotting Oranges │ │ └── optimal.py │ ├── 3. Leetcode 200 - Number of Islands │ │ ├── bfs.py │ │ └── dfs.py │ ├── 4. Leetcode 733 - Flood Fill │ │ ├── bfs.py │ │ └── dfs.py │ ├── 5. Detect Cycle in an Undirected Graph (using BFS) │ │ └── bfs.py │ └── 6. Cycle Detection in undirected Graph (dfs) │ │ └── dfs.py └── 4. Topo Sort based problems │ ├── 1. Topo Sort │ ├── dfs.py │ └── kahns-algorithm-bfs.py │ ├── 2. Cycle Detection in Directed Graph (BFS) │ └── optimal.py │ ├── 3. Course Schedule - I │ └── optimal.py │ └── 4. Course Schedule - II │ └── optimal.py ├── 17. Bit Manipulation ├── 1. Introduction │ ├── 1. number to binary.py │ └── 2. binary to decimal.py └── 2. Basic Problems │ ├── 1. Swap numbers.py │ ├── 2. Check if ith bit is set or not.py │ ├── 3. Minimum number of flips to convert.py │ ├── 4. Single number.py │ ├── 5. Power-set.py │ └── 6. xor from left to right.py ├── 18. Greedy Algorithms ├── 1. Easy │ ├── 1. Assign Cookies │ │ └── 1. optimal.py │ ├── 2. Fractional Knapsack │ │ └── 1. optimal.py │ └── 4. Lemonade Change │ │ └── optimal.py └── 2. Medium or Hard │ ├── 1. N meetings in one room │ └── optimal.py │ ├── 2. Jump Game │ └── optimal.py │ ├── 3. Jump Game 2 │ ├── 1. recursion.py │ ├── 2. memoization.py │ ├── 3. tabulation.py │ ├── 4. tabulation [space-optimize].py │ └── 5. greedy.py │ ├── 4. Minimum Platforms │ ├── 1. brute.py │ └── 2. optimal.py │ └── 5. Candy │ ├── 1. brute-force.py │ ├── 2. better.py │ └── 3. optimal.py ├── 2. Recursion ├── basics.py ├── factorial.py ├── fibonacci.py ├── palindrome.py ├── power.py ├── print_1_to_n.py ├── print_n_times.py ├── print_n_to_1.py ├── reverse_an_array.py └── sum_of_1_to_n.py ├── 20. Strings └── 1. Easy │ ├── 1. Leetcode 1021 - Remove Outermost Parentheses │ └── optimal.py │ ├── 2. Leetcode 151 - Reverse Words in a String │ ├── brute-force.py │ └── optimal.py │ ├── 3. Leetcode 1903 - Largest Odd Number in String │ └── optimal.py │ ├── 4. Leetcode 14 - Longest Common Prefix │ └── optimal.py │ ├── 6. Leetcode 796 - Rotate String │ ├── brute-force.py │ └── optimal.py │ └── 7. Leetcode 242 - Valid Anagram │ ├── brute-force.py │ └── optimal.py ├── 21. Binary Search Trees ├── 1. Introduciton │ ├── 1. Search in a Binary Search Tree │ │ └── optimal.py │ └── 2. Find Min or Max in BST │ │ └── optimal.py └── 2. Practice Problems │ └── 1. Ceil in a Binary Search Tree │ └── optimal.py ├── 3. Hashing ├── character-hashing.py ├── dictionary(numbers).py ├── dictionary(string).py ├── number-hashing.py ├── question1.py └── question2.py ├── 4. Sorting ├── Coding Ninjas Solution │ ├── bubble-sort.py │ ├── insertion-sort.py │ └── selection-sort.py ├── bubble-sort.py ├── insertion-sort.py ├── merge-sort-reverse.py ├── merge-sort.py ├── quick-sort.py └── selection-sort.py ├── 5. Lists ├── Easy │ ├── 1. Largest element in the array │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 10. Merge 2 Sorted Array │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 11. Leetcode 268 – Missing Number │ │ ├── better.py │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 12. Leetcode 485 - Max Consecutive Ones │ │ └── optimal.py │ ├── 2. Second Largest Number │ │ ├── better.py │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 3. Check Sorted Array │ │ └── optimal.py │ ├── 4. Leetcodee - 1752 - Check if Array Is Sorted and Rotated │ │ └── optimal.py │ ├── 5. Leetcode - 26 -Remove Duplicates from Sorted Array │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 6. Left Rotate array by one │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 7. Leetcode 189 - Rotate Array │ │ ├── better.py │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 8. Leetcode 283 - Move Zeroes │ │ ├── brute-force.py │ │ └── optimal.py │ └── 9. Linear Seach │ │ └── optimal.py ├── Hard │ ├── Count Inversions │ │ ├── brute-force.py │ │ └── optimal.py │ ├── Find the repeating and missing numbers │ │ ├── better.py │ │ ├── brute-force.py │ │ └── optimal.py │ ├── Leetcode 15 - 3Sum │ │ ├── better.py │ │ ├── brute-force.py │ │ └── optimal.py │ ├── Leetcode 152 - Maximum Product Subarray │ │ └── brute-force.py │ ├── Leetcode 18 - 4Sum │ │ ├── better.py │ │ ├── brute-force.py │ │ └── optimal.py │ ├── Leetcode 493 - Reverse Pairs │ │ ├── brute-force.py │ │ └── optimal.py │ ├── Leetcode 56 - Merge Intervals │ │ ├── brute-force.py │ │ └── optimal.py │ ├── Majority Element 2 │ │ ├── better.py │ │ ├── brute-force.py │ │ └── optimal.py │ ├── Max Subarray with Sum 0 │ │ ├── brute-force.py │ │ └── optimal.py │ ├── Merge two sorted arrays without extra space │ │ ├── brute-force.py │ │ └── optimal.py │ └── Pascals Triangle │ │ └── variation-3-optimal.py └── Medium │ ├── 1. Longest Subarray With Sum K │ ├── better.py │ ├── brute-force-2.py │ ├── brute-force.py │ └── optimal.py │ ├── 10. Superior Elements │ └── optimal.py │ ├── 11. Longest Successive Elements │ ├── better.py │ ├── brute.py │ └── optimal.py │ ├── 12. Leetcode 73 - Set Matrix Zeros │ ├── better.py │ ├── brute-force.py │ └── optimal.py │ ├── 13. Leetcode 54 - Spiral Matrix │ └── optimal.py │ ├── 14 . Leetcode 48 - Rotate Image │ ├── brute-force.py │ └── optimal.py │ ├── 2. Longest Subarray With Sum K │ ├── better.py │ ├── brute-force-2.py │ └── brute-force.py │ ├── 3. Leetcode 1 - Two Sum │ ├── brute-force.py │ └── optimal.py │ ├── 4. Leetcode 75 - Sort Colors │ ├── better.py │ ├── brute.py │ └── optimal.py │ ├── 5. Leetcode 169 - Majority Element │ ├── better.py │ ├── brute.py │ └── optimal.py │ ├── 6. Leetcode 53 - Maximum Subarray │ ├── better.py │ ├── brute-force.py │ └── optimal.py │ ├── 7. Leetcode 121 - Best Time to Buy and Sell Stock │ ├── brute-force.py │ └── optimal.py │ ├── 8. Leetcode 2149 - Rearrange Array Elements by Sign │ ├── brute.py │ └── optimal.py │ ├── 9. Leetcode 31 - Next Permutation │ └── optimal.py.py │ └── Permutation │ ├── approach-1.py │ └── approach-2.py ├── 6. Binary Search ├── Easy │ ├── 1. Leetcode 35 - Search Insert Position │ │ └── optimal.py │ ├── 2. Ceil The Floor │ │ └── optimal.py │ ├── 3. Leetcode 34 - Find First and Last Position of Element in Sorted Array │ │ ├── brute-force.py │ │ └── optimal.py │ └── 4. Leetcode 704 - Binary Search │ │ └── recursive.py └── Medium │ ├── 1. Number of occurrence │ ├── brute-force.py │ └── optimal.py │ ├── 2. Leetcode 33 – Search in Rotated Sorted Array │ ├── brute-force.py │ └── optimal.py │ ├── 3. Leetcode 81 - Search in Rotated Sorted Array II │ ├── brute-force.py │ └── optimal.py │ ├── 4. Leetcode 153 - Find Minimum in Rotated Sorted Array │ ├── brute-force.py │ ├── optimal1.py │ └── optimal2.py │ ├── 5. Rotation │ ├── brute-force.py │ └── optimal.py │ ├── 6. Leetcode 540 - Single Element in a Sorted Array │ ├── brute.py │ ├── optimal1.py │ └── optimal2.py │ └── 7. Leetcode 162 - Find Peak Element │ ├── brute.py │ └── optimal.py ├── 7. Binary Search on Answers ├── 1. Square Root of a number │ ├── brute-force.py │ └── optimal.py ├── 10. Kth Missing Positive Number │ └── brute-force.py ├── 2. Find Nth Root Of M │ ├── brute-force.py │ └── optimal.py ├── 3. Leetcode 875 - Koko Eating Bananas │ ├── brute-force.py │ └── optimal.py ├── 4. Leetcode 1482 - Minimum Number of Days to Make m Bouquets │ └── optimal.py ├── 5. Leetcode 1283 - Find the Smallest Divisor Given a Threshold │ ├── brute-force.py │ └── optimal.py ├── 6. Leetcode 1011 - Capacity To Ship Packages Within D Days │ ├── brute-force.py │ └── optimal.py ├── 7. Aggressive Cows │ ├── 1. linear.py │ └── 2. binary-search.py ├── 8. Allocate Books │ ├── 1. linear.py │ └── 2. binary-search.py └── 9. Split Array Largest Sum │ └── 1. binary.py ├── 8. Singly Linked List ├── 1. Introduction │ ├── 1. Introduction To Linked List.py │ ├── 2. Insert Node At The Beginning.py │ ├── 3. Delete Node Of Linked List.py │ ├── 4. Count nodes of linked list.py │ ├── 5. Search in a Linked List.py │ └── 6. Leetcode 707 - Design Linked List.py ├── 2. Easy │ ├── 1. Leetcode 876 - Middle of the Linked List │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 2. Leetcode 206 - Reverse Linked List │ │ ├── brute-force.py │ │ ├── iterative-approach.py │ │ └── recursive-approach.py │ └── 3. Leetcode 141 - Linked List Cycle │ │ ├── brute-force.py │ │ └── optimal.py ├── 3. Medium │ ├── 1. Leetcode 142 - Linked List Cycle II │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 10. Add 1 to a Linked List Number │ │ └── brute-force.py │ ├── 11. Add 2 numbers in LL │ │ └── optimal.py │ ├── 2. Find length of Loop │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 3. Odd and Even List │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 4. Leetcode 234 - Palindrome Linked List │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 5. Leetcode 19 - Remove Nth Node From End of List │ │ ├── brute-force.py │ │ └── optimal.py │ ├── 6. Leetcode 2095 - Delete the Middle Node of a Linked List │ │ └── optimal.py │ ├── 7. Leetcode 148 - Sort List │ │ ├── brute-force.py │ │ └── merge-sort.py │ ├── 8. Sort a linked list of 0s, 1s and 2s │ │ ├── brute-force.py │ │ └── optimal.py │ └── 9. Intersection of Two Linked Lists │ │ ├── better.py │ │ ├── brute-force.py │ │ └── optimal.py ├── 4. Hard │ ├── 1. Leetcode 25 - Reverse Nodes in k-Group │ │ └── optimal.py │ └── 2. Leetcode 61 - Rotate List │ │ ├── brute.py │ │ └── optimal.py └── sll.py ├── 9. Doubly Linked List └── Medium │ ├── 1. Delete all occurrences of a given key in a doubly linked list │ ├── optimal.py │ └── optimal2.py │ ├── 2. Find pairs with given sum in doubly linked list │ ├── better.py │ ├── brute.py │ └── optimal.py │ ├── 3. Remove duplicates from a sorted Doubly Linked List │ └── optimal.py │ ├── 4. Reverse a Doubly Linked List │ ├── brute-force.py │ └── optimal.py │ └── dll.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | test.py -------------------------------------------------------------------------------- /1. Basics/1. Count Digits/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity = O(log10 n) where n is the digit 3 | Space Complexity = O(1) 4 | """ 5 | 6 | 7 | def count_digits(num: int) -> int: 8 | count = 0 9 | n = num 10 | while n > 0: 11 | count += 1 12 | n = n // 10 13 | return count 14 | 15 | 16 | print(count_digits(1234)) 17 | -------------------------------------------------------------------------------- /1. Basics/1. Count Digits/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity = O(1) 3 | Space Complexity = O(1) 4 | """ 5 | 6 | import math 7 | 8 | 9 | def count_digits(num: int) -> int: 10 | return math.floor(math.log10(num) + 1) 11 | 12 | 13 | print(count_digits(1234)) 14 | -------------------------------------------------------------------------------- /1. Basics/2. Reverse a number/leetcode-solution.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def reverse(self, x: int) -> int: 3 | reverse_number = 0 4 | is_negative = False 5 | if x < 0: 6 | is_negative = True 7 | # x=x * -1 8 | x = abs(x) 9 | while x > 0: 10 | last_digit = x % 10 11 | reverse_number = (reverse_number * 10) + last_digit 12 | x = x // 10 13 | if is_negative: 14 | reverse_number = reverse_number * -1 15 | if reverse_number < (-(2**31)) or reverse_number > (2**31 - 1): 16 | return 0 17 | return reverse_number 18 | -------------------------------------------------------------------------------- /1. Basics/2. Reverse a number/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity = O(log10 n), n is the number 3 | Space Complexity = O(1) 4 | """ 5 | 6 | 7 | def reverseNumberOptimal(num: int) -> int: 8 | n = num 9 | reversed_number = 0 10 | while n > 0: 11 | last_digit = n % 10 12 | reversed_number = (reversed_number * 10) + last_digit 13 | n = n // 10 14 | return reversed_number 15 | 16 | 17 | print(reverseNumberOptimal(1234)) 18 | -------------------------------------------------------------------------------- /1. Basics/3. Print divisors/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Brute Force 3 | TC - O(n), where n is the number 4 | SC - O(d), where d is the number of divisors of the input integer n 5 | """ 6 | 7 | from typing import List 8 | 9 | 10 | def printDivisors(n: int) -> List[int]: 11 | result = [] 12 | for i in range(1, n + 1): 13 | if n % i == 0: 14 | result.append(i) 15 | return result 16 | -------------------------------------------------------------------------------- /1. Basics/3. Print divisors/question.py: -------------------------------------------------------------------------------- 1 | """ 2 | Question Link 3 | 4 | https://www.codingninjas.com/studio/problems/print-all-divisors-of-a-number_1164188?leftPanelTabValue=PROBLEM 5 | """ 6 | -------------------------------------------------------------------------------- /1. Basics/4. Check prime/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | TC -> O(n) where n is the number 3 | SC -> O(1) 4 | """ 5 | 6 | 7 | def checkPrime(num: int) -> int: 8 | for i in range(2, num): 9 | if num % i == 0: 10 | return False 11 | return True 12 | 13 | 14 | print(checkPrime(2)) 15 | print(checkPrime(3)) 16 | print(checkPrime(10)) 17 | -------------------------------------------------------------------------------- /1. Basics/4. Check prime/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | TC -> O(sqrt(n)) where n is the number 3 | SC -> O(1) 4 | """ 5 | 6 | from math import sqrt 7 | 8 | 9 | def checkPrime(num: int) -> int: 10 | if num == 1: 11 | return False 12 | for i in range(2, int(sqrt(num)) + 1): 13 | if num % i == 0: 14 | return False 15 | return True 16 | 17 | 18 | print(checkPrime(2)) 19 | print(checkPrime(3)) 20 | print(checkPrime(10)) 21 | -------------------------------------------------------------------------------- /1. Basics/4. Check prime/question.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://www.codingninjas.com/studio/problems/check-prime_624934 3 | """ 4 | -------------------------------------------------------------------------------- /1. Basics/5. Check Palindrome/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isPalindrome(self, x: int) -> bool: 3 | if x < 0: 4 | return False 5 | num = x 6 | reverse_number = 0 7 | while num > 0: 8 | reverse_number = (reverse_number * 10) + num % 10 9 | num = num // 10 10 | return reverse_number == x 11 | -------------------------------------------------------------------------------- /1. Basics/6. Sum of all divisors/better.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | 4 | def sumOfAllDivisors(n: int) -> int: 5 | Sum = 0 6 | for i in range(1, n + 1): 7 | for j in range(1, int(math.sqrt(i)) + 1): 8 | if i % j == 0: 9 | Sum += j 10 | if i // j != j: 11 | Sum += i // j 12 | return Sum 13 | -------------------------------------------------------------------------------- /1. Basics/6. Sum of all divisors/brute-force.py: -------------------------------------------------------------------------------- 1 | def sumOfAllDivisors(n: int) -> int: 2 | Sum = 0 3 | for i in range(1, n + 1): 4 | for j in range(1, i + 1): 5 | if i % j == 0: 6 | Sum += j 7 | return Sum 8 | -------------------------------------------------------------------------------- /1. Basics/6. Sum of all divisors/optimal.py: -------------------------------------------------------------------------------- 1 | def sumOfAllDivisors(n: int) -> int: 2 | Sum = 0 3 | for i in range(1, n + 1): 4 | Sum += i * (n // i) 5 | return Sum 6 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/1. Leetcode 3 - Longest Substring Without Repeating Characters/brute-force.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def lengthOfLongestSubstring(self, s: str) -> int: 3 | if len(s) == 0: 4 | return 0 5 | maxans = 0 6 | for i in range(len(s)): 7 | set = {} 8 | for j in range(i, len(s)): 9 | if s[j] in set: 10 | break 11 | maxans = max(maxans, j - i + 1) 12 | set[s[j]] = 1 13 | return maxans 14 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/1. Leetcode 3 - Longest Substring Without Repeating Characters/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def lengthOfLongestSubstring(self, s: str) -> int: 3 | hash_map = dict() 4 | left = 0 5 | right = 0 6 | length = 0 7 | n = len(s) 8 | while right < n: 9 | if s[right] in hash_map: 10 | left = max(hash_map[s[right]] + 1, left) 11 | hash_map[s[right]] = right 12 | length = max(length, right - left + 1) 13 | right += 1 14 | return length 15 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/2. Leetcode 1004 - Max Consecutive Ones III/better.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) + O(N) -> O(2N) 3 | N is the number of elements in nums 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | from typing import List 9 | 10 | 11 | class Solution: 12 | def longestOnes(self, nums: List[int], k: int) -> int: 13 | max_length = 0 14 | left = 0 15 | right = 0 16 | zeros = 0 17 | n = len(nums) 18 | while right < n: 19 | if nums[right] == 0: 20 | zeros += 1 21 | while zeros > k: 22 | if nums[left] == 0: 23 | zeros -= 1 24 | left += 1 25 | if zeros <= k: 26 | length = right - left + 1 27 | max_length = max(max_length, length) 28 | right += 1 29 | return max_length 30 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/2. Leetcode 1004 - Max Consecutive Ones III/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def longestOnes(self, nums: List[int], k: int) -> int: 6 | max_length = 0 7 | n = len(nums) 8 | for i in range(n): 9 | zeros = 0 10 | for j in range(i, n): 11 | if nums[j] == 0: 12 | zeros += 1 13 | if zeros <= k: 14 | length = j - i + 1 15 | max_length = max(max_length, length) 16 | else: 17 | break 18 | return max_length 19 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/2. Leetcode 1004 - Max Consecutive Ones III/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) 3 | N is the number of elements in nums 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | from typing import List 9 | 10 | 11 | class Solution: 12 | def longestOnes(self, nums: List[int], k: int) -> int: 13 | left = 0 14 | right = 0 15 | n = len(nums) 16 | zeros = 0 17 | max_length = 0 18 | while right < n: 19 | if nums[right] == 0: 20 | zeros += 1 21 | if zeros > k: 22 | if nums[left] == 0: 23 | zeros -= 1 24 | left += 1 25 | if zeros <= k: 26 | length = right - left + 1 27 | max_length = max(max_length, length) 28 | right += 1 29 | return max_length 30 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/3. Fruits and Baskets/better.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N) + O(N) 5 | N is the number of elements in arr 6 | 7 | Space complexity -> O(3) 8 | """ 9 | 10 | 11 | def findMaxFruits(arr: List[int], n: int) -> int: 12 | fruits = {} 13 | left = 0 14 | right = 0 15 | n = len(arr) 16 | max_length = 0 17 | while right < n: 18 | fruits[arr[right]] = fruits.get(arr[right], 0) + 1 19 | while len(fruits) > 2: 20 | fruits[arr[left]] -= 1 21 | if fruits[arr[left]] == 0: 22 | del fruits[arr[left]] 23 | left += 1 24 | max_length = max(max_length, right - left + 1) 25 | right = right + 1 26 | return max_length 27 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/3. Fruits and Baskets/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N^2) 5 | N is the number of elements in arr 6 | 7 | Space complexity -> O(3) 8 | """ 9 | 10 | 11 | def findMaxFruits(arr: List[int], n: int) -> int: 12 | n = len(arr) 13 | max_length = 0 14 | for i in range(n): 15 | fruits = set() 16 | for j in range(i, n): 17 | fruits.add(arr[j]) 18 | if len(fruits) > 2: 19 | break 20 | max_length = max(max_length, j - i + 1) 21 | return max_length 22 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/3. Fruits and Baskets/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N) 5 | N is the number of elements in arr 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | def findMaxFruits(arr: List[int], n: int) -> int: 12 | fruits = {} 13 | n = len(arr) 14 | left = 0 15 | right = 0 16 | max_length = 0 17 | while right < n: 18 | fruits[arr[right]] = fruits.get(arr[right], 0) + 1 19 | if len(fruits) > 2: 20 | fruits[arr[left]] -= 1 21 | if fruits[arr[left]] == 0: 22 | del fruits[arr[left]] 23 | left += 1 24 | if len(fruits) <= 2: 25 | max_length = max(max_length, right - left + 1) 26 | right += 1 27 | return max_length 28 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/4. Leetcode 424 - Longest Repeating Character Replacement/better.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def characterReplacement(self, s: str, k: int) -> int: 3 | max_length = 0 4 | n = len(s) 5 | left = 0 6 | right = 0 7 | hash_map = dict() 8 | max_freq = 0 9 | while right < n: 10 | hash_map[s[right]] = hash_map.get(s[right], 0) + 1 11 | max_freq = max(max_freq, hash_map[s[right]]) 12 | while right - left + 1 - max_freq > k: 13 | hash_map[s[left]] -= 1 14 | max_freq = 0 15 | for v in hash_map.values(): 16 | max_freq = max(max_freq, v) 17 | left += 1 18 | 19 | max_length = max(max_length, right - left + 1) 20 | right += 1 21 | return max_length 22 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/4. Leetcode 424 - Longest Repeating Character Replacement/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N^2) 3 | N is the number of characters in s 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | class Solution: 10 | def characterReplacement(self, s: str, k: int) -> int: 11 | max_length = 0 12 | n = len(s) 13 | for i in range(0, n): 14 | hash_map = dict() 15 | max_freq = 0 16 | for j in range(i, n): 17 | hash_map[s[j]] = hash_map.get(s[j], 0) + 1 18 | max_freq = max(max_freq, hash_map[s[j]]) 19 | changes = (j - i + 1) - max_freq 20 | if changes <= k: 21 | max_length = max(max_length, j - i + 1) 22 | else: 23 | break 24 | return max_length 25 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/4. Leetcode 424 - Longest Repeating Character Replacement/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def characterReplacement(self, s: str, k: int) -> int: 3 | max_length = 0 4 | n = len(s) 5 | left = 0 6 | right = 0 7 | hash_map = dict() 8 | max_freq = 0 9 | while right < n: 10 | hash_map[s[right]] = hash_map.get(s[right], 0) + 1 11 | max_freq = max(max_freq, hash_map[s[right]]) 12 | while right - left + 1 - max_freq > k: 13 | hash_map[s[left]] -= 1 14 | left += 1 15 | 16 | max_length = max(max_length, right - left + 1) 17 | right += 1 18 | return max_length 19 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/5. Leetcode 930 - Binary Subarrays With Sum/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N^2) 5 | N is the number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def numSubarraysWithSum(self, nums: List[int], goal: int) -> int: 13 | count = 0 14 | n = len(nums) 15 | for i in range(n): 16 | total = 0 17 | for j in range(i, n): 18 | total += nums[j] 19 | if total > goal: 20 | break 21 | if total == goal: 22 | count += 1 23 | return count 24 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/5. Leetcode 930 - Binary Subarrays With Sum/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(2 * 2N) 5 | N is the number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def countSubArrayLessThanOrEqualToGoal(self, nums, goal): 13 | if goal < 0: 14 | return 0 15 | count = 0 16 | n = len(nums) 17 | left = 0 18 | right = 0 19 | Sum = 0 20 | while right < n: 21 | Sum += nums[right] 22 | while Sum > goal: 23 | Sum -= nums[left] 24 | left += 1 25 | count = count + ((right - left) + 1) 26 | right += 1 27 | return count 28 | 29 | def numSubarraysWithSum(self, nums: List[int], goal: int) -> int: 30 | return self.countSubArrayLessThanOrEqualToGoal( 31 | nums, goal 32 | ) - self.countSubArrayLessThanOrEqualToGoal(nums, goal - 1) 33 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/6. Leetcode 1248 - Count Number of Nice Subarrays/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(2 * 2N) 5 | N is the number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def countSubArrayLessThanOrEqualToGoal(self, nums, goal): 13 | if goal < 0: 14 | return 0 15 | count = 0 16 | n = len(nums) 17 | left = 0 18 | right = 0 19 | Sum = 0 20 | while right < n: 21 | Sum += nums[right] % 2 22 | while Sum > goal: 23 | Sum -= nums[left] % 2 24 | left += 1 25 | count = count + ((right - left) + 1) 26 | right += 1 27 | return count 28 | 29 | def numberOfSubarrays(self, nums: List[int], k: int) -> int: 30 | return self.countSubArrayLessThanOrEqualToGoal( 31 | nums, k 32 | ) - self.countSubArrayLessThanOrEqualToGoal(nums, k - 1) 33 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/7. Leetcode 1358 - Number of Substrings Containing All Three Characters/better.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N^2) 3 | N is the number of characters in s 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | # This is a slight optimization 9 | # But still the time complexity will be same 10 | # Its just a small optimization 11 | 12 | 13 | class Solution: 14 | def numberOfSubstrings(self, s: str) -> int: 15 | count = 0 16 | n = len(s) 17 | for i in range(n): 18 | chars = set() 19 | for j in range(i, n): 20 | chars.add(s[j]) 21 | if len(chars) == 3: 22 | count += n - j 23 | break 24 | return count 25 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/7. Leetcode 1358 - Number of Substrings Containing All Three Characters/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N^2) 3 | N is the number of characters in s 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | class Solution: 10 | def numberOfSubstrings(self, s: str) -> int: 11 | count = 0 12 | n = len(s) 13 | for i in range(n): 14 | chars = set() 15 | for j in range(i, n): 16 | chars.add(s[j]) 17 | if len(chars) == 3: 18 | count += 1 19 | return count 20 | -------------------------------------------------------------------------------- /10. Sliding Window/Medium/7. Leetcode 1358 - Number of Substrings Containing All Three Characters/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) 3 | N is the number of characters in s 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | class Solution: 10 | def numberOfSubstrings(self, s: str) -> int: 11 | count = 0 12 | n = len(s) 13 | i = 0 14 | # Try to use List instead of this 15 | numbers = {"a": -1, "b": -1, "c": -1} 16 | while i < n: 17 | numbers[s[i]] = i 18 | if numbers["a"] >= 0 and numbers["b"] >= 0 and numbers["c"] >= 0: 19 | count += min(numbers.values()) + 1 20 | i += 1 21 | return count 22 | -------------------------------------------------------------------------------- /11. Stack and Queues/1. stack_using_arrays.py: -------------------------------------------------------------------------------- 1 | class Stack: 2 | def __init__(self): 3 | self.items = [] 4 | 5 | def is_empty(self): 6 | return len(self.items) == 0 7 | 8 | def push(self, item): 9 | self.items.append(item) 10 | 11 | def pop(self): 12 | if len(self.items) == 0: 13 | return "Cannot pop, stack is empty" 14 | x = self.items.pop() 15 | return x 16 | 17 | def top(self): 18 | if len(self.items) == 0: 19 | return "Cannot top, stack is empty" 20 | return self.items[-1] 21 | 22 | def size(self): 23 | return len(self.items) 24 | 25 | 26 | stack = Stack() 27 | stack.push(5) 28 | stack.push(10) 29 | stack.push(15) 30 | print(f"Stack content = {stack}") 31 | print(f"Popped item = {stack.pop()}") 32 | print(f"Stack content = {stack}") 33 | print(f"Top item after pop = {stack.peek()}") 34 | print(f"Stack is empty = {stack.is_empty()}") 35 | -------------------------------------------------------------------------------- /11. Stack and Queues/deque_module.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | q = deque() 4 | print(q) 5 | q.append(100) 6 | q.append(200) 7 | print(q) 8 | q.appendleft(300) 9 | print(q) 10 | q.pop() 11 | print(q) 12 | q.popleft() 13 | print(q) 14 | -------------------------------------------------------------------------------- /11. Stack and Queues/postfix,infix,prefxi conversion/3. postfix-to-infix.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def postToInfix(self, s): 3 | # Stack to store operands 4 | stack = [] 5 | 6 | for char in s: 7 | # If character is an operand, push it to the stack 8 | if char.isalnum(): 9 | stack.append(char) 10 | else: 11 | # Pop two operands 12 | operand2 = stack.pop() 13 | operand1 = stack.pop() 14 | 15 | # Combine operands with the operator 16 | new_expr = f"({operand1}{char}{operand2})" 17 | 18 | # Push the result back onto the stack 19 | stack.append(new_expr) 20 | 21 | # The final element in the stack is the infix expression 22 | return stack[-1] 23 | -------------------------------------------------------------------------------- /11. Stack and Queues/postfix,infix,prefxi conversion/4. prefix-to-infix.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def preToInfix(self, s): 3 | # Stack to store operands 4 | stack = [] 5 | 6 | for char in s[::-1]: 7 | # If character is an operand, push it to the stack 8 | if char.isalnum(): 9 | stack.append(char) 10 | else: 11 | # Pop two operands but with reversed order 12 | operand1 = stack.pop() 13 | operand2 = stack.pop() 14 | 15 | # Combine operands with the operator 16 | new_expr = f"({operand1}{char}{operand2})" 17 | 18 | # Push the result back onto the stack 19 | stack.append(new_expr) 20 | 21 | # The final element in the stack is the infix expression 22 | return stack[-1] 23 | -------------------------------------------------------------------------------- /11. Stack and Queues/postfix,infix,prefxi conversion/5. postfix-to-prefix.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def postToPre(self, s): 3 | # Stack to store operands 4 | stack = [] 5 | 6 | # Process each character in postfix expression 7 | for char in s: 8 | # If the character is an operand, push it to the stack 9 | if char.isalnum(): 10 | stack.append(char) 11 | else: 12 | # Pop two operands from the stack 13 | operand2 = stack.pop() 14 | operand1 = stack.pop() 15 | 16 | # Combine the operands with the operator in prefix form 17 | new_expr = f"{char}{operand1}{operand2}" 18 | 19 | # Push the result back onto the stack 20 | stack.append(new_expr) 21 | 22 | # The final element in the stack is the prefix expression 23 | return stack[-1] 24 | -------------------------------------------------------------------------------- /11. Stack and Queues/postfix,infix,prefxi conversion/6. prefix-to-postfix.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def preToPost(self, s): 3 | # Stack to store operands 4 | stack = [] 5 | 6 | # Traverse the prefix expression from right to left using index 7 | n = len(s) 8 | for i in range(n - 1, -1, -1): # Reverse iteration using index 9 | char = s[i] 10 | 11 | # If the character is an operand, push it to the stack 12 | if char.isalnum(): 13 | stack.append(char) 14 | else: 15 | # Pop two operands from the stack 16 | operand1 = stack.pop() 17 | operand2 = stack.pop() 18 | 19 | # Combine the operands with the operator in postfix form 20 | new_expr = operand1 + operand2 + char 21 | 22 | # Push the result back onto the stack 23 | stack.append(new_expr) 24 | 25 | # The final element in the stack is the postfix expression 26 | return stack[-1] 27 | -------------------------------------------------------------------------------- /11. Stack and Queues/questions/3. Leetcode 20 - Valid Parentheses/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) 3 | N is the number of brackets 4 | 5 | Space complexity -> O(N) 6 | """ 7 | 8 | 9 | class Solution: 10 | def isValid(self, s: str) -> bool: 11 | stack = [] 12 | for bracket in s: 13 | if bracket == "(" or bracket == "{" or bracket == "[": 14 | stack.append(bracket) 15 | else: 16 | if len(stack) == 0: 17 | return False 18 | ch = stack.pop() 19 | if ( 20 | (bracket == ")" and ch == "(") 21 | or (bracket == "]" and ch == "[") 22 | or (bracket == "}" and ch == "{") 23 | ): 24 | continue 25 | else: 26 | return False 27 | return len(stack) == 0 28 | -------------------------------------------------------------------------------- /11. Stack and Queues/questions/4. Leetcode 155 - Min Stack/optimal.py: -------------------------------------------------------------------------------- 1 | class MinStack: 2 | 3 | def __init__(self): 4 | self.items = [] 5 | 6 | def push(self, val: int) -> None: 7 | if not self.items: 8 | self.items.append([val, val]) 9 | else: 10 | mini = min(val, self.items[-1][-1]) 11 | self.items.append([val, mini]) 12 | 13 | def pop(self) -> None: 14 | if self.items: 15 | self.items.pop() 16 | 17 | def top(self) -> int: 18 | if len(self.items) == 0: 19 | return 0 20 | return self.items[-1][0] 21 | 22 | def getMin(self) -> int: 23 | if len(self.items) == 0: 24 | return 0 25 | return self.items[-1][-1] 26 | 27 | 28 | # Your MinStack object will be instantiated and called as such: 29 | # obj = MinStack() 30 | # obj.push(val) 31 | # obj.pop() 32 | # param_3 = obj.top() 33 | # param_4 = obj.getMin() 34 | -------------------------------------------------------------------------------- /11. Stack and Queues/questions/5. Leetcode 496 - Next Greater Element I/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def nextGreaterElements(self, nums: List[int]) -> List[int]: 6 | n = len(nums) 7 | result = [-1] * n 8 | stack = [] 9 | 10 | for i in range(2 * n - 1, -1, -1): 11 | while len(stack) != 0 and stack[-1] <= nums[i % n]: 12 | stack.pop() 13 | 14 | if i < n: 15 | if stack: 16 | result[i] = stack[-1] 17 | stack.append(nums[i % n]) 18 | return result 19 | -------------------------------------------------------------------------------- /11. Stack and Queues/questions/6. Leetcode 503 - Next Greater Element II/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def nextGreaterElements(self, nums: List[int]) -> List[int]: 6 | n = len(nums) 7 | result = [-1] * n 8 | stack = [] 9 | 10 | for i in range(2 * n - 1, -1, -1): 11 | while stack and stack[-1] <= nums[i % n]: 12 | stack.pop() 13 | 14 | if i < n: 15 | if stack: 16 | result[i] = stack[-1] 17 | stack.append(nums[i % n]) 18 | return result 19 | -------------------------------------------------------------------------------- /12. Recursion/generate_non_cont_sequences.py: -------------------------------------------------------------------------------- 1 | def func(subset: list, index: int): 2 | if index >= len(nums): 3 | result.append(subset.copy()) 4 | return 5 | subset.append(nums[index]) 6 | func(subset, index + 1) 7 | subset.pop() 8 | func(subset, index + 1) 9 | 10 | 11 | result = [] 12 | nums = [45, 73, 11, 91] 13 | func([], 0) 14 | print(result) 15 | -------------------------------------------------------------------------------- /13. Advance Recursion/Medium/1. Leetcode 78 - Subsets/code.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time Complexity: O(2^n) 5 | 6 | Space Complexity: O(n) -> recursion stack 7 | """ 8 | 9 | 10 | class Solution: 11 | def subsets(self, nums: List[int]) -> List[List[int]]: 12 | ans = [] 13 | 14 | def backtrack(subset, idx): 15 | if idx >= len(nums): 16 | ans.append(subset[:]) 17 | return 18 | subset.append(nums[idx]) 19 | backtrack(subset, idx + 1) 20 | subset.pop() 21 | backtrack(subset, idx + 1) 22 | 23 | backtrack([], 0) 24 | return ans 25 | -------------------------------------------------------------------------------- /13. Advance Recursion/Medium/10. Leetcode 17 - Letter Combinations of a Phone Number/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def __init__(self): 3 | self.digits_to_letters = { 4 | "2": "abc", 5 | "3": "def", 6 | "4": "ghi", 7 | "5": "jkl", 8 | "6": "mno", 9 | "7": "pqrs", 10 | "8": "tuv", 11 | "9": "wxyz", 12 | } 13 | 14 | def helper(self, digits, ans, index, current): 15 | if index == len(digits): 16 | ans.append(current) 17 | return 18 | 19 | letters = self.digits_to_letters.get(digits[index], "") 20 | for letter in letters: 21 | self.helper(digits, ans, index + 1, current + letter) 22 | 23 | def letterCombinations(self, digits): 24 | ans = [] 25 | if not digits: 26 | return ans 27 | self.helper(digits, ans, 0, "") 28 | return ans 29 | -------------------------------------------------------------------------------- /13. Advance Recursion/Medium/3. Leetcode 40 - Combination Sum II/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def backtrack(self, subset: List[int], index: int, target: int, result, candidates): 6 | if target == 0: 7 | result.add(tuple(subset.copy())) 8 | return 9 | elif target < 0: 10 | return 11 | if index >= len(candidates): 12 | return 13 | subset.append(candidates[index]) 14 | target -= candidates[index] 15 | self.backtrack(subset, index + 1, target, result, candidates) 16 | subset.pop() 17 | target += candidates[index] 18 | self.backtrack(subset, index + 1, target, result, candidates) 19 | 20 | def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: 21 | candidates.sort() 22 | result = set() 23 | self.backtrack([], 0, target, result, candidates) 24 | return list(result) 25 | -------------------------------------------------------------------------------- /13. Advance Recursion/Medium/3. Leetcode 40 - Combination Sum II/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def backtrack(self, subset, index, target, result, candidates): 6 | if target == 0: 7 | result.append(subset.copy()) 8 | return 9 | for i in range(index, len(candidates)): 10 | if i > index and candidates[i] == candidates[i - 1]: 11 | continue 12 | if candidates[i] > target: 13 | break 14 | subset.append(candidates[i]) 15 | self.backtrack(subset, i + 1, target - candidates[i], result, candidates) 16 | subset.pop() 17 | 18 | def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: 19 | candidates.sort() 20 | result = [] 21 | self.backtrack([], 0, target, result, candidates) 22 | return result 23 | -------------------------------------------------------------------------------- /13. Advance Recursion/Medium/4. Subset Sum/brute-force.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def solve(self, nums, index, subset, result): 3 | if index >= len(nums): 4 | result.append(sum(subset)) 5 | return 6 | subset.append(nums[index]) 7 | self.solve(nums, index + 1, subset, result) 8 | subset.pop() 9 | self.solve(nums, index + 1, subset, result) 10 | 11 | def subsetSums(self, arr): 12 | result = [] 13 | self.solve(arr, 0, [], result) 14 | return result 15 | -------------------------------------------------------------------------------- /13. Advance Recursion/Medium/4. Subset Sum/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def solve(self, nums, total, index, result): 3 | if index >= len(nums): 4 | result.append(total) 5 | return 6 | Sum = total + nums[index] 7 | self.solve(nums, Sum, index + 1, result) 8 | Sum = total 9 | self.solve(nums, Sum, index + 1, result) 10 | 11 | def subsetSums(self, arr): 12 | result = [] 13 | self.solve(arr, 0, 0, result) 14 | return result 15 | -------------------------------------------------------------------------------- /13. Advance Recursion/Medium/5. Leetcode 90 - Subsets II/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: 6 | def backtrack(subset, index): 7 | result.append(subset.copy()) 8 | for i in range(index, len(nums)): 9 | if i > index and nums[i] == nums[i - 1]: 10 | continue 11 | subset.append(nums[i]) 12 | backtrack(subset, i + 1) 13 | subset.pop() 14 | 15 | result = [] 16 | nums.sort() 17 | backtrack([], 0) 18 | return result 19 | -------------------------------------------------------------------------------- /13. Advance Recursion/Medium/9. Combination Sum 3/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def func(self, n, Sum, last, nums, k, ans): 3 | if Sum == n and len(nums) == k: 4 | ans.append(list(nums)) 5 | return 6 | if Sum > n or len(nums) > k: 7 | return 8 | 9 | for i in range(last, 10): 10 | nums.append(i) 11 | self.func(n, Sum + i, i + 1, nums, k, ans) 12 | nums.pop() 13 | 14 | def combinationSum3(self, k, n): 15 | ans = [] 16 | nums = [] 17 | self.func(n, 0, 1, nums, k, ans) 18 | return ans 19 | -------------------------------------------------------------------------------- /13. Advance Recursion/count_subsequences (all).py: -------------------------------------------------------------------------------- 1 | """ 2 | Count the number of subsequences where sum=K. 3 | Time complexity -> O(2^n) 4 | """ 5 | 6 | from typing import List 7 | 8 | 9 | def backtrack(subset: List[int], index: int, total: int): 10 | if total == k: 11 | return 1 12 | elif total > k: 13 | return 0 14 | if index >= len(nums): 15 | return 0 16 | subset.append(nums[index]) 17 | Sum = total + nums[index] 18 | left = backtrack(subset, index + 1, Sum) 19 | e = subset.pop() 20 | Sum -= e 21 | right = backtrack(subset, index + 1, Sum) 22 | return left + right 23 | 24 | 25 | nums = [1, 2, 3, 4, 3, 2, 1, 1, 1, 1] 26 | k = 3 27 | count = backtrack([], 0, 0) 28 | print(count) 29 | -------------------------------------------------------------------------------- /13. Advance Recursion/print_subsequences (all).py: -------------------------------------------------------------------------------- 1 | """ 2 | Print all the subsequences where sum=K. 3 | Generate every one of them 4 | 5 | Time complexity -> O(2^n) 6 | """ 7 | 8 | from typing import List 9 | 10 | 11 | def backtrack(subset: List[int], index: int, total: int): 12 | if total == k: 13 | result.append(subset.copy()) 14 | return 15 | elif total > k: 16 | return 17 | if index >= len(nums): 18 | return 19 | subset.append(nums[index]) 20 | Sum = total + nums[index] 21 | backtrack(subset, index + 1, Sum) 22 | subset.pop() 23 | Sum = total 24 | backtrack(subset, index + 1, Sum) 25 | 26 | 27 | result = [] 28 | nums = [1, 2, 3, 4, 3, 2, 1, 1, 1, 1] 29 | k = 3 30 | backtrack([], 0, 0) 31 | print(result) 32 | -------------------------------------------------------------------------------- /13. Advance Recursion/print_subsequences (one).py: -------------------------------------------------------------------------------- 1 | """ 2 | Print all the subsequences where sum=K. 3 | Generate only 1 4 | 5 | Time complexity -> O(2^n) 6 | """ 7 | 8 | from typing import List 9 | 10 | 11 | def backtrack(subset: List[int], index: int, total: int): 12 | if total == k: 13 | result.append(subset.copy()) 14 | return True 15 | if index >= len(nums): 16 | return False 17 | subset.append(nums[index]) 18 | Sum = total + nums[index] 19 | pick = backtrack(subset, index + 1, Sum) 20 | if pick == True: 21 | return True 22 | e = subset.pop() 23 | Sum -= e 24 | return backtrack(subset, index + 1, Sum) 25 | 26 | 27 | result = [] 28 | nums = [1, 2, 3, 4, 3, 2, 1, 1, 1, 1] 29 | k = 3 30 | backtrack([], 0, 0) 31 | print(result) 32 | -------------------------------------------------------------------------------- /14. Binary Trees/1. Traversals/2. bfs.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class Node: 5 | def __init__(self, data): 6 | self.data = data 7 | self.left = None 8 | self.right = None 9 | 10 | 11 | def level_order_traversal(root): 12 | if root is None: 13 | return 14 | 15 | result = [] 16 | 17 | queue = deque() 18 | queue.append(root) 19 | 20 | while queue: 21 | # level_size = len(queue) 22 | node = queue.popleft() 23 | result.append(node.data) 24 | 25 | if node.left is not None: 26 | queue.append(node.left) 27 | if node.right is not None: 28 | queue.append(node.right) 29 | 30 | return result 31 | 32 | 33 | root = Node(1) 34 | root.left = Node(2) 35 | root.right = Node(3) 36 | root.left.left = Node(4) 37 | root.left.right = Node(5) 38 | root.right.left = Node(6) 39 | root.right.right = Node(7) 40 | 41 | print("Level order traversal:") 42 | print(level_order_traversal(root)) 43 | -------------------------------------------------------------------------------- /14. Binary Trees/1. Traversals/4. Iterative Inorder.py: -------------------------------------------------------------------------------- 1 | class TreeNode: 2 | def __init__(self, val=0, left=None, right=None): 3 | self.val = val 4 | self.left = left 5 | self.right = right 6 | 7 | 8 | def inorderTraversal(root): 9 | stack = [] 10 | result = [] 11 | current = root 12 | 13 | while current or stack: 14 | while current: 15 | stack.append(current) 16 | current = current.left 17 | 18 | current = stack.pop() 19 | result.append(current.val) 20 | current = current.right 21 | 22 | return result 23 | 24 | 25 | root = TreeNode(1) 26 | root.left = TreeNode(2) 27 | root.right = TreeNode(3) 28 | 29 | print(inorderTraversal(root)) 30 | -------------------------------------------------------------------------------- /14. Binary Trees/1. Traversals/6. Iterative PostOrder (Using 1 stack).py: -------------------------------------------------------------------------------- 1 | class TreeNode: 2 | def __init__(self, val=0, left=None, right=None): 3 | self.val = val 4 | self.left = left 5 | self.right = right 6 | 7 | 8 | def postorderTraversal(root): 9 | stack = [] 10 | result = [] 11 | current = root 12 | prev = None 13 | 14 | while current or stack: 15 | while current: 16 | stack.append(current) 17 | current = current.left 18 | 19 | current = stack[-1] 20 | 21 | if current.right is None or current.right == prev: 22 | prev = stack.pop() 23 | result.append(prev.val) 24 | current = None 25 | else: 26 | current = current.right 27 | 28 | return result 29 | 30 | 31 | root = TreeNode(1) 32 | root.left = TreeNode(2) 33 | root.right = TreeNode(3) 34 | 35 | print(postorderTraversal(root)) 36 | -------------------------------------------------------------------------------- /14. Binary Trees/2. Easy and Medium/1. Leetcode 104 - Maximum Depth of Binary Tree/optimal [Recursive].py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(N) 3 | where N height. 4 | 5 | Space Complexity: O(N) 6 | where N height. 7 | """ 8 | 9 | 10 | # Definition for a binary tree node. 11 | class TreeNode: 12 | def __init__(self, val=0, left=None, right=None): 13 | self.val = val 14 | self.left = left 15 | self.right = right 16 | 17 | 18 | from typing import Optional 19 | 20 | 21 | class Solution: 22 | def solve(self, node): 23 | if node == None: 24 | return 0 25 | leftHeight = self.solve(node.left) 26 | rightHeight = self.solve(node.right) 27 | return 1 + max(leftHeight, rightHeight) 28 | 29 | def maxDepth(self, root: Optional[TreeNode]) -> int: 30 | return self.solve(root) 31 | -------------------------------------------------------------------------------- /14. Binary Trees/2. Easy and Medium/10. Leetcode 101 - Symmetric Tree/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class TreeNode: 5 | def __init__(self, val=0, left=None, right=None): 6 | self.val = val 7 | self.left = left 8 | self.right = right 9 | 10 | 11 | class Solution: 12 | 13 | def check(self, root1, root2): 14 | if (root1 and not root2) or (root2 and not root1): 15 | return False 16 | elif not root1 and not root2: 17 | return True 18 | if root1.val != root2.val: 19 | return False 20 | return self.check(root1.left, root2.right) and self.check( 21 | root1.right, root2.left 22 | ) 23 | 24 | def isSymmetric(self, root: Optional[TreeNode]) -> bool: 25 | if not root: 26 | return True 27 | return self.check(root.left, root.right) 28 | -------------------------------------------------------------------------------- /14. Binary Trees/2. Easy and Medium/3. Leetcode 543 - Diameter of Binary Tree/optimal.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | class TreeNode: 3 | def __init__(self, val=0, left=None, right=None): 4 | self.val = val 5 | self.left = left 6 | self.right = right 7 | 8 | 9 | """ 10 | Time Complexity: O(N) 11 | Space Complexity : O(1) as no additional data structures or 12 | memory is allocated.O(H) 13 | """ 14 | from typing import Optional 15 | 16 | 17 | class Solution: 18 | diameter = 0 19 | 20 | def calculateHeight(self, root): 21 | if not root: 22 | return 0 23 | leftHeight = self.calculateHeight(root.left) 24 | rightHeight = self.calculateHeight(root.right) 25 | self.diameter = max(self.diameter, leftHeight + rightHeight) 26 | return 1 + max(leftHeight, rightHeight) 27 | 28 | def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int: 29 | self.calculateHeight(root) 30 | return self.diameter 31 | -------------------------------------------------------------------------------- /14. Binary Trees/2. Easy and Medium/4. Leetcode 124 - Binary Tree Maximum Path Sum/optimal.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | class TreeNode: 3 | def __init__(self, val=0, left=None, right=None): 4 | self.val = val 5 | self.left = left 6 | self.right = right 7 | 8 | 9 | from typing import Optional 10 | 11 | 12 | class Solution: 13 | maxi = float("-inf") 14 | 15 | def findMaxPathSum(self, root): 16 | if not root: 17 | return 0 18 | leftPathSum = max(0, self.findMaxPathSum(root.left)) 19 | rightPathSum = max(0, self.findMaxPathSum(root.right)) 20 | 21 | self.maxi = max(self.maxi, leftPathSum + root.val + rightPathSum) 22 | return max(leftPathSum, rightPathSum) + root.val 23 | 24 | def maxPathSum(self, root: Optional[TreeNode]) -> int: 25 | self.findMaxPathSum(root) 26 | return self.maxi 27 | -------------------------------------------------------------------------------- /14. Binary Trees/2. Easy and Medium/5. Leetcode 100 - Same Tree/optimal.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | class TreeNode: 3 | def __init__(self, val=0, left=None, right=None): 4 | self.val = val 5 | self.left = left 6 | self.right = right 7 | 8 | 9 | from typing import Optional 10 | 11 | 12 | class Solution: 13 | def check(self, node1, node2): 14 | if node1 is None and node2 is None: 15 | return True 16 | if node1 is None or node2 is None: 17 | return False 18 | if node1.val != node2.val: 19 | return False 20 | leftSide = self.check(node1.left, node2.left) 21 | if leftSide == False: 22 | return False 23 | rightSide = self.check(node1.right, node2.right) 24 | if rightSide == False: 25 | return False 26 | return True 27 | 28 | def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: 29 | return self.check(p, q) 30 | -------------------------------------------------------------------------------- /14. Binary Trees/2. Easy and Medium/7. Top View of Binary Tree/optimal.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class Solution: 5 | 6 | # Function to return a list of nodes visible from the top view 7 | # from left to right in Binary Tree. 8 | def topView(self, root): 9 | if not root: 10 | return None 11 | ans = [] 12 | queue = deque() 13 | element_with_level = {} 14 | queue.append((root, 0)) 15 | while queue: 16 | e, line = queue.popleft() 17 | if line not in element_with_level: 18 | element_with_level[line] = e.data 19 | if e.left: 20 | queue.append((e.left, line - 1)) 21 | if e.right: 22 | queue.append((e.right, line + 1)) 23 | for value in sorted(element_with_level.items()): 24 | ans.append(value[1]) 25 | return ans 26 | -------------------------------------------------------------------------------- /14. Binary Trees/2. Easy and Medium/8. Bottom View of Binary Tree/optimal.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class Solution: 5 | def bottomView(self, root): 6 | # code here 7 | if not root: 8 | return None 9 | ans = [] 10 | queue = deque() 11 | element_with_level = {} 12 | queue.append((root, 0)) 13 | while queue: 14 | e, line = queue.popleft() 15 | element_with_level[line] = e.data 16 | if e.left: 17 | queue.append((e.left, line - 1)) 18 | if e.right: 19 | queue.append((e.right, line + 1)) 20 | for value in sorted(element_with_level.items()): 21 | ans.append(value[1]) 22 | return ans 23 | -------------------------------------------------------------------------------- /14. Binary Trees/2. Easy and Medium/9. Leetcode 199 - Binary Tree Right Side View/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, List 2 | from collections import deque 3 | 4 | 5 | class TreeNode: 6 | def __init__(self, val=0, left=None, right=None): 7 | self.val = val 8 | self.left = left 9 | self.right = right 10 | 11 | 12 | class Solution: 13 | def rightSideView(self, root: Optional[TreeNode]) -> List[int]: 14 | if not root: 15 | return [] 16 | 17 | result = [] 18 | queue = deque([root]) 19 | 20 | while queue: 21 | level_size = len(queue) 22 | for i in range(level_size): 23 | node = queue.popleft() 24 | if i == level_size - 1: # Rightmost node in this level 25 | result.append(node.val) 26 | if node.left: 27 | queue.append(node.left) 28 | if node.right: 29 | queue.append(node.right) 30 | 31 | return result 32 | -------------------------------------------------------------------------------- /14. Binary Trees/2. Easy and Medium/9. Leetcode 199 - Binary Tree Right Side View/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, List 2 | 3 | 4 | class TreeNode: 5 | def __init__(self, val=0, left=None, right=None): 6 | self.val = val 7 | self.left = left 8 | self.right = right 9 | 10 | 11 | class Solution: 12 | def reversePostOrder(self, root, level, result): 13 | if not root: 14 | return None 15 | if len(result) == level: 16 | result.append(root.val) 17 | if root.right: 18 | self.reversePostOrder(root.right, level + 1, result) 19 | if root.left: 20 | self.reversePostOrder(root.left, level + 1, result) 21 | 22 | def rightSideView(self, root: Optional[TreeNode]) -> List[int]: 23 | result = [] 24 | self.reversePostOrder(root, 0, result) 25 | return result 26 | -------------------------------------------------------------------------------- /15. Dynamic Programming/1. Introduction/1. fibonacci [memo].py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) 3 | Space complexity -> O(N) + O(N) 4 | """ 5 | 6 | 7 | def fibonacci(num, dp): 8 | if num <= 1: 9 | return num 10 | if dp[num] != -1: 11 | return dp[num] 12 | dp[num] = fibonacci(num - 1, dp) + fibonacci(num - 2, dp) 13 | return dp[num] 14 | 15 | 16 | if __name__ == "__main__": 17 | n = 5 18 | dp = [-1] * (n + 1) 19 | print(fibonacci(n, dp)) 20 | -------------------------------------------------------------------------------- /15. Dynamic Programming/1. Introduction/2. fibonacci [tabulation].py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) 3 | Space complexity -> O(N) 4 | """ 5 | 6 | n = 6 7 | dp = [-1] * (n + 1) 8 | dp[0] = 0 9 | dp[1] = 1 10 | for i in range(2, n + 1): 11 | dp[i] = dp[i - 1] + dp[i - 2] 12 | 13 | print(dp[n]) 14 | -------------------------------------------------------------------------------- /15. Dynamic Programming/1. Introduction/3. fibonacci [optimized].py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) 3 | Space complexity -> O(1) 4 | """ 5 | 6 | n = 6 7 | prev2 = 0 8 | prev = 1 9 | for i in range(2, n + 1): 10 | curr = prev2 + prev 11 | prev2 = prev 12 | prev = curr 13 | 14 | print(prev) 15 | -------------------------------------------------------------------------------- /15. Dynamic Programming/2. 1D-DP/1. Leetcode 70 - Climbing Stairs/1. tabulation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(N) 3 | Reason: We are running a simple iterative loop 4 | 5 | Space Complexity: O(N) 6 | Reason: We are using an external array of size (n+1) 7 | """ 8 | 9 | 10 | class Solution: 11 | 12 | def climbStairs(self, n: int) -> int: 13 | dp = [-1] * (n + 1) 14 | dp[0] = 1 15 | dp[1] = 1 16 | for i in range(2, n + 1): 17 | dp[i] = dp[i - 1] + dp[i - 2] 18 | return dp[n] 19 | -------------------------------------------------------------------------------- /15. Dynamic Programming/2. 1D-DP/1. Leetcode 70 - Climbing Stairs/2. tabulation [optimized].py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(N) 3 | Reason: We are running a simple iterative loop 4 | 5 | Space Complexity: O(1) 6 | Reason: We are not using any extra space. 7 | """ 8 | 9 | 10 | class Solution: 11 | 12 | def climbStairs(self, n: int) -> int: 13 | prev2 = 1 14 | prev = 1 15 | for i in range(2, n + 1): 16 | curr = prev2 + prev 17 | prev2 = prev 18 | prev = curr 19 | return prev 20 | -------------------------------------------------------------------------------- /15. Dynamic Programming/2. 1D-DP/2. Frog Jump/3. tabulation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(N) 3 | Reason: We are running a simple iterative loop 4 | 5 | Space Complexity: O(N) 6 | Reason: We are using an external array of size 'n+1'. 7 | """ 8 | 9 | 10 | class Solution: 11 | def minimumEnergy(self, height, n): 12 | dp = [-1] * n 13 | dp[0] = 0 14 | for i in range(1, n): 15 | jump2 = float("inf") 16 | jump1 = dp[i - 1] + abs(height[i] - height[i - 1]) 17 | if i > 1: 18 | jump2 = dp[i - 2] + abs(height[i] - height[i - 2]) 19 | dp[i] = min(jump1, jump2) 20 | return dp[n - 1] 21 | -------------------------------------------------------------------------------- /15. Dynamic Programming/2. 1D-DP/2. Frog Jump/4. tabulation [space optimized].py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(N) 3 | Reason: We are running a simple iterative loop 4 | 5 | Space Complexity: O(1) 6 | Reason: We are not using any extra space. 7 | """ 8 | 9 | 10 | class Solution: 11 | def minimumEnergy(self, height, n): 12 | prev = 0 13 | prev2 = 0 14 | for i in range(1, n): 15 | jump2 = float("inf") 16 | jump1 = prev + abs(height[i] - height[i - 1]) 17 | if i > 1: 18 | jump2 = prev2 + abs(height[i] - height[i - 2]) 19 | cur_i = min(jump1, jump2) 20 | prev2 = prev 21 | prev = cur_i 22 | return prev 23 | -------------------------------------------------------------------------------- /15. Dynamic Programming/2. 1D-DP/3. Minimal Cost/2. tabulation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(N*K) 3 | Reason: We are running two nested loops, where outer loops run from 1 to n-1 4 | and the inner loop runs from 1 to K 5 | 6 | Space Complexity: O(N) 7 | Reason: We are using an external array of size 'n' 8 | """ 9 | 10 | 11 | class Solution: 12 | def minimizeCost(self, height, n, k): 13 | dp = [-1] * n 14 | dp[0] = 0 15 | for i in range(1, n): 16 | minimumSteps = float("inf") 17 | for j in range(1, k + 1): 18 | if i - j >= 0: 19 | jump = dp[i - j] + abs(height[i] - height[i - j]) 20 | minimumSteps = min(jump, minimumSteps) 21 | dp[i] = minimumSteps 22 | 23 | return dp[n - 1] 24 | -------------------------------------------------------------------------------- /15. Dynamic Programming/2. 1D-DP/4. Leetcode 198 - House Robber/1. recursion.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def solve(self, index, nums): 6 | if index == 0: 7 | return nums[index] 8 | if index == -1: 9 | return 0 10 | pick = nums[index] + self.solve(index - 2, nums) 11 | notpick = 0 + self.solve(index - 1, nums) 12 | return max(pick, notpick) 13 | 14 | def rob(self, nums: List[int]) -> int: 15 | n = len(nums) 16 | return self.solve(n - 1, nums) 17 | -------------------------------------------------------------------------------- /15. Dynamic Programming/2. 1D-DP/4. Leetcode 198 - House Robber/3. tabulation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(N) 3 | Reason: We are running a simple iterative loop 4 | 5 | Space Complexity: O(N) 6 | Reason: We are using an external array of size 'n+1'. 7 | """ 8 | 9 | from typing import List 10 | 11 | 12 | class Solution: 13 | 14 | def rob(self, nums: List[int]) -> int: 15 | n = len(nums) 16 | dp = [-1] * n 17 | dp[0] = nums[0] 18 | for index in range(1, n): 19 | pick = nums[index] 20 | if index > 1: 21 | pick += dp[index - 2] 22 | notpick = 0 + dp[index - 1] 23 | dp[index] = max(pick, notpick) 24 | return dp[n - 1] 25 | -------------------------------------------------------------------------------- /15. Dynamic Programming/2. 1D-DP/4. Leetcode 198 - House Robber/4. tabulation [space optimize].py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(N) 3 | Reason: We are running a simple iterative loop 4 | 5 | Space Complexity: O(1) 6 | """ 7 | 8 | from typing import List 9 | 10 | 11 | class Solution: 12 | 13 | def rob(self, nums: List[int]) -> int: 14 | n = len(nums) 15 | prev2 = 0 16 | prev = nums[0] 17 | for index in range(1, n): 18 | pick = nums[index] 19 | if index > 1: 20 | pick += prev2 21 | notpick = 0 + prev 22 | curr = max(pick, notpick) 23 | prev2 = prev 24 | prev = curr 25 | return prev 26 | -------------------------------------------------------------------------------- /15. Dynamic Programming/2. 1D-DP/5. Leetcode - 213 House Robber II/tabulation [space optimized].py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(N) 3 | Reason: We are running a simple iterative loop, two times. 4 | Therefore total time complexity will be O(N) + O(N) ≈ O(N) 5 | 6 | Space Complexity: O(1) 7 | Reason: We are not using extra space. 8 | """ 9 | 10 | from typing import List 11 | 12 | 13 | class Solution: 14 | 15 | def solve(self, nums: List[int]) -> int: 16 | n = len(nums) 17 | prev2 = 0 18 | prev = nums[0] 19 | for index in range(1, n): 20 | pick = nums[index] 21 | if index > 1: 22 | pick += prev2 23 | notpick = 0 + prev 24 | curr = max(pick, notpick) 25 | prev2 = prev 26 | prev = curr 27 | return prev 28 | 29 | def rob(self, nums: List[int]) -> int: 30 | if len(nums) == 1: 31 | return nums[0] 32 | ans1 = self.solve(nums[1:]) 33 | ans2 = self.solve(nums[:-1]) 34 | return max(ans1, ans2) 35 | -------------------------------------------------------------------------------- /15. Dynamic Programming/3. DP on 2D array/1. Geek's Training/1. recursion.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def solve(self, day, last, points): 3 | # Base case: When we reach day 0, return the maximum point for the last day. 4 | if day == 0: 5 | maxi = 0 6 | for i in range(3): 7 | if i != last: 8 | maxi = max(maxi, points[0][i]) 9 | return maxi 10 | 11 | maxi = 0 12 | # Iterate through all activities for the current day. 13 | for i in range(3): 14 | if i != last: 15 | # Calculate the total points for the current day's activity and recursively call for the previous day. 16 | activity = points[day][i] + self.solve(day - 1, i, points) 17 | maxi = max(maxi, activity) 18 | 19 | return maxi 20 | 21 | def maximumPoints(self, points, n): 22 | return self.solve(n - 1, 3, points) 23 | -------------------------------------------------------------------------------- /15. Dynamic Programming/3. DP on 2D array/2. Leetcode 62 - Unique Paths/1. recursion.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(2 ^ (m x n)) 3 | Space Complexity: O(M-1) + O(N-1) 4 | """ 5 | 6 | 7 | class Solution: 8 | def solve(self, i, j): 9 | if i == 0 and j == 0: 10 | return 1 11 | if i < 0 or j < 0: 12 | return 0 13 | up = self.solve(i - 1, j) 14 | left = self.solve(i, j - 1) 15 | return up + left 16 | 17 | def uniquePaths(self, m: int, n: int) -> int: 18 | return self.solve(m - 1, n - 1) 19 | -------------------------------------------------------------------------------- /15. Dynamic Programming/3. DP on 2D array/2. Leetcode 62 - Unique Paths/2. memoization.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(M*N) 3 | Reason: At max, there will be M*N calls of recursion. 4 | 5 | Space Complexity: O((N-1)+(M-1)) + O(M*N) 6 | """ 7 | 8 | 9 | class Solution: 10 | def solve(self, i, j, dp): 11 | if dp[i][j] != -1: 12 | return dp[i][j] 13 | if i == 0 and j == 0: 14 | return 1 15 | if i < 0 or j < 0: 16 | return 0 17 | up = self.solve(i - 1, j, dp) 18 | left = self.solve(i, j - 1, dp) 19 | dp[i][j] = up + left 20 | return dp[i][j] 21 | 22 | def uniquePaths(self, m: int, n: int) -> int: 23 | dp = [[-1 for j in range(n)] for i in range(m)] 24 | return self.solve(m - 1, n - 1, dp) 25 | -------------------------------------------------------------------------------- /15. Dynamic Programming/3. DP on 2D array/2. Leetcode 62 - Unique Paths/3. tabulation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(M*N) 3 | Space Complexity: O(M*N) 4 | """ 5 | 6 | 7 | class Solution: 8 | def uniquePaths(self, m: int, n: int) -> int: 9 | dp = [[-1 for j in range(n)] for i in range(m)] 10 | for i in range(m): 11 | for j in range(n): 12 | if i == 0 and j == 0: 13 | dp[i][j] = 1 14 | continue 15 | up = 0 16 | left = 0 17 | if i > 0: 18 | up = dp[i - 1][j] 19 | if j > 0: 20 | left = dp[i][j - 1] 21 | dp[i][j] = up + left 22 | return dp[m - 1][n - 1] 23 | -------------------------------------------------------------------------------- /15. Dynamic Programming/3. DP on 2D array/2. Leetcode 62 - Unique Paths/4. tabulation [space-optmize].py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(M*N) 3 | Reason: There are two nested loops 4 | 5 | Space Complexity: O(N) 6 | Reason: We are using an external array of size 'N' to store only one row. 7 | """ 8 | 9 | 10 | class Solution: 11 | def uniquePaths(self, m: int, n: int) -> int: 12 | prev = [0] * n 13 | for i in range(m): 14 | temp = [0] * n 15 | for j in range(n): 16 | if i == 0 and j == 0: 17 | temp[j] = 1 18 | continue 19 | up = 0 20 | left = 0 21 | if i > 0: 22 | up = prev[j] 23 | if j > 0: 24 | left = temp[j - 1] 25 | temp[j] = up + left 26 | prev = temp 27 | return prev[n - 1] 28 | -------------------------------------------------------------------------------- /15. Dynamic Programming/4. DP on Subsequence or Subarray/1. Subset sum equal to target/1. recursion.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def solve(self, index, target, arr): 3 | if target == 0: 4 | return True 5 | if index == 0: 6 | return arr[index] == target 7 | notpick = self.solve(index - 1, target, arr) 8 | pick = False 9 | if arr[index] <= target: 10 | pick = self.solve(index - 1, target - arr[index], arr) 11 | return pick or notpick 12 | 13 | def isSubsetSum(self, N, arr, sum): 14 | return self.solve(N - 1, sum, arr) 15 | -------------------------------------------------------------------------------- /15. Dynamic Programming/4. DP on Subsequence or Subarray/1. Subset sum equal to target/2. memoization.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def solve(self, index, target, arr, dp): 3 | if target == 0: 4 | return True 5 | if index == 0: 6 | return arr[index] == target 7 | if dp[index][target] != -1: 8 | return dp[index][target] 9 | notpick = self.solve(index - 1, target, arr, dp) 10 | pick = False 11 | if arr[index] <= target: 12 | pick = self.solve(index - 1, target - arr[index], arr, dp) 13 | dp[index][target] = pick or notpick 14 | return dp[index][target] 15 | 16 | def isSubsetSum(self, N, arr, sum): 17 | dp = [[-1 for _ in range(sum + 1)] for _ in range(N)] 18 | return self.solve(N - 1, sum, arr, dp) 19 | -------------------------------------------------------------------------------- /15. Dynamic Programming/5. DP on Stocks/1. Leetcode 121 - Best Time to Buy and Sell Stock/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def maxProfit(self, prices: List[int]) -> int: 6 | max_profit = 0 7 | min_price = prices[0] 8 | n = len(prices) 9 | for i in range(1, n): 10 | profit = prices[i] - min_price 11 | max_profit = max(max_profit, profit) 12 | min_price = min(min_price, prices[i]) 13 | return max_profit 14 | -------------------------------------------------------------------------------- /15. Dynamic Programming/5. DP on Stocks/2. Leetcode 122 - Best Time to Buy and Sell Stock II/1. recursion.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def solve(self, index, buy, prices): 6 | if index == len(prices): 7 | return 0 8 | if buy: 9 | buy_profit = -prices[index] + self.solve(index + 1, 0, prices) 10 | notbuy_profit = 0 + self.solve(index + 1, 1, prices) 11 | profit = max(buy_profit, notbuy_profit) 12 | else: 13 | sell_profit = prices[index] + self.solve(index + 1, 1, prices) 14 | notsell_profit = 0 + self.solve(index + 1, 0, prices) 15 | profit = max(sell_profit, notsell_profit) 16 | return profit 17 | 18 | def maxProfit(self, prices: List[int]) -> int: 19 | return self.solve(0, 1, prices) 20 | -------------------------------------------------------------------------------- /15. Dynamic Programming/5. DP on Stocks/2. Leetcode 122 - Best Time to Buy and Sell Stock II/4. tabulation [space-optimize].py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(N*2) 3 | Reason: There are two nested loops that account for O(N*2) complexity 4 | 5 | Space Complexity: O(1) 6 | 7 | Reason: We are using an external array of size 2 8 | """ 9 | 10 | from typing import List 11 | 12 | 13 | class Solution: 14 | def maxProfit(self, prices: List[int]) -> int: 15 | n = len(prices) 16 | ahead = [0, 0] # Represents dp[ind + 1][0] and dp[ind + 1][1] 17 | curr = [0, 0] # Represents dp[ind][0] and dp[ind][1] 18 | 19 | for ind in range(n - 1, -1, -1): 20 | for buy in range(2): 21 | if buy: 22 | curr[buy] = max(-prices[ind] + ahead[0], ahead[1]) 23 | else: 24 | curr[buy] = max(prices[ind] + ahead[1], ahead[0]) 25 | ahead = curr[:] # Move current values to ahead 26 | 27 | return ahead[1] # Correct return statement 28 | -------------------------------------------------------------------------------- /15. Dynamic Programming/5. DP on Stocks/5. Leetcode 309 - Best Time to Buy and Sell Stock with Cooldown/1. recursion.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def solve(self, index, buy, prices): 6 | if index >= len(prices): 7 | return 0 8 | if buy: 9 | buy_profit = -prices[index] + self.solve(index + 1, 0, prices) 10 | notbuy_profit = 0 + self.solve(index + 1, 1, prices) 11 | profit = max(buy_profit, notbuy_profit) 12 | else: 13 | sell_profit = prices[index] + self.solve(index + 2, 1, prices) 14 | notsell_profit = 0 + self.solve(index + 1, 0, prices) 15 | profit = max(sell_profit, notsell_profit) 16 | return profit 17 | 18 | def maxProfit(self, prices: List[int]) -> int: 19 | return self.solve(0, 1, prices) 20 | -------------------------------------------------------------------------------- /15. Dynamic Programming/5. DP on Stocks/6. Leetcode 714 - Best Time to Buy and Sell Stock with Transaction Fee/1. recursion.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def solve(self, index, buy, prices, fee): 6 | if index == len(prices): 7 | return 0 8 | if buy: 9 | buy_profit = -prices[index] + self.solve(index + 1, 0, prices, fee) 10 | notbuy_profit = 0 + self.solve(index + 1, 1, prices, fee) 11 | profit = max(buy_profit, notbuy_profit) 12 | else: 13 | sell_profit = prices[index] - fee + self.solve(index + 1, 1, prices, fee) 14 | notsell_profit = 0 + self.solve(index + 1, 0, prices, fee) 15 | profit = max(sell_profit, notsell_profit) 16 | return profit 17 | 18 | def maxProfit(self, prices: List[int], fee: int) -> int: 19 | return self.solve(0, 1, prices, fee) 20 | -------------------------------------------------------------------------------- /15. Dynamic Programming/6. DP on Strings/1. Leetcode 1143 - Longest Common Subsequence/1. recursion.py: -------------------------------------------------------------------------------- 1 | """ 2 | Precise Time Complexity: O(2^(m + n)), 3 | where m and n are the lengths of text1 and text2, respectively. 4 | 5 | Rounded Time Complexity: O(2^n), 6 | where n is the length of the longer string between text1 and text2. 7 | 8 | Space Complexity: O(N*M) + O(N+M) 9 | 10 | Reason: We are using an auxiliary recursion stack space(O(N+M)) and a 2D array ( O(N*M)). 11 | """ 12 | 13 | 14 | class Solution: 15 | def solve(self, s1, s2, ind1, ind2): 16 | if ind1 < 0 or ind2 < 0: 17 | return 0 18 | if s1[ind1] == s2[ind2]: 19 | return 1 + self.solve(s1, s2, ind1 - 1, ind2 - 1) 20 | else: 21 | p1 = self.solve(s1, s2, ind1 - 1, ind2) 22 | p2 = self.solve(s1, s2, ind1, ind2 - 1) 23 | return max(p1, p2) 24 | 25 | def longestCommonSubsequence(self, text1: str, text2: str) -> int: 26 | return self.solve(text1, text2, len(text1) - 1, len(text2) - 1) 27 | -------------------------------------------------------------------------------- /15. Dynamic Programming/6. DP on Strings/3. Longest Common Substring/tabulation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(N*M) 3 | Reason: There are two nested loops 4 | 5 | Space Complexity: O(N*M) 6 | Reason: We are using an external array of size 'N*M'. Stack Space is eliminated. 7 | """ 8 | 9 | 10 | class Solution: 11 | def longestCommonSubsequence(self, text1: str, text2: str) -> int: 12 | n = len(text1) 13 | m = len(text2) 14 | dp = [[-1 for _ in range(m + 1)] for _ in range(n + 1)] 15 | ans = 0 16 | for i in range(n + 1): 17 | dp[i][0] = 0 18 | for j in range(m + 1): 19 | dp[0][j] = 0 20 | for ind1 in range(1, n + 1): 21 | for ind2 in range(1, m + 1): 22 | if text1[ind1 - 1] == text2[ind2 - 1]: 23 | dp[ind1][ind2] = 1 + dp[ind1 - 1][ind2 - 1] 24 | ans = max(ans, dp[ind1][ind2]) 25 | else: 26 | dp[ind1][ind2] = 0 27 | return ans 28 | -------------------------------------------------------------------------------- /15. Dynamic Programming/6. DP on Strings/4. Leetcode 516 - Longest Palindromic Subsequence/1. recursion.py: -------------------------------------------------------------------------------- 1 | """ 2 | Precise Time Complexity: O(2^(n*n)), 3 | 4 | Space Complexity: O(n+n) + O(n+n) 5 | Reason: We are using an auxiliary recursion stack space(O(N+M)) and a 2D array ( O(N*M)). 6 | """ 7 | 8 | 9 | class Solution: 10 | def solve(self, s1, s2, ind1, ind2): 11 | if ind1 < 0 or ind2 < 0: 12 | return 0 13 | if s1[ind1] == s2[ind2]: 14 | return 1 + self.solve(s1, s2, ind1 - 1, ind2 - 1) 15 | else: 16 | return max( 17 | self.solve(s1, s2, ind1 - 1, ind2), self.solve(s1, s2, ind1, ind2 - 1) 18 | ) 19 | 20 | def longestPalindromeSubseq(self, s: str) -> int: 21 | n = len(s) 22 | return self.solve(s, s[::-1], n - 1, n - 1) 23 | -------------------------------------------------------------------------------- /15. Dynamic Programming/6. DP on Strings/4. Leetcode 516 - Longest Palindromic Subsequence/2. memoization.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: O(N*N) 3 | Space Complexity: O(N*N) + O(N+N) 4 | Reason: We are using an auxiliary recursion stack space(O(N+N)) 5 | and a 2D array ( O(N*N)). 6 | """ 7 | 8 | 9 | class Solution: 10 | def solve(self, s1, s2, ind1, ind2, dp): 11 | if ind1 < 0 or ind2 < 0: 12 | return 0 13 | if dp[ind1][ind2] != -1: 14 | return dp[ind1][ind2] 15 | if s1[ind1] == s2[ind2]: 16 | return 1 + self.solve(s1, s2, ind1 - 1, ind2 - 1, dp) 17 | else: 18 | dp[ind1][ind2] = max( 19 | self.solve(s1, s2, ind1 - 1, ind2, dp), 20 | self.solve(s1, s2, ind1, ind2 - 1, dp), 21 | ) 22 | return dp[ind1][ind2] 23 | 24 | def longestPalindromeSubseq(self, s: str) -> int: 25 | n = len(s) 26 | dp = [[-1 for _ in range(n)] for _ in range(n)] 27 | return self.solve(s, s[::-1], n - 1, n - 1, dp) 28 | -------------------------------------------------------------------------------- /16. Graphs/1. Introduction/adjacent-matrix.py: -------------------------------------------------------------------------------- 1 | connections = [[2, 1], [1, 3], [2, 4], [3, 4], [2, 5], [4, 5]] 2 | n = 5 3 | 4 | matrix = [[0 for _ in range(n + 1)] for _ in range(n + 1)] 5 | 6 | for n1, n2 in connections: 7 | matrix[n1][n2] = 1 8 | matrix[n2][n1] = 1 9 | 10 | print(matrix) 11 | -------------------------------------------------------------------------------- /16. Graphs/1. Introduction/listss.py: -------------------------------------------------------------------------------- 1 | connections = [[2, 1], [1, 3], [2, 4], [3, 4], [2, 5], [4, 5]] 2 | n = 5 3 | 4 | lst = [[] for _ in range(n + 1)] 5 | 6 | for n1, n2 in connections: 7 | lst[n1].append(n2) 8 | lst[n2].append(n1) 9 | 10 | print(lst) 11 | -------------------------------------------------------------------------------- /16. Graphs/3. Questions/1. Leetcode 547 - Number of Provinces/sol1.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | # Without creating List 4 | 5 | 6 | class Solution: 7 | def dfs(self, node, adj, visited): 8 | visited[node] = 1 9 | for n in range(len(adj)): 10 | if adj[node][n] == 1 and visited[n] == 0: 11 | self.dfs(n, adj, visited) 12 | 13 | def findCircleNum(self, isConnected: List[List[int]]) -> int: 14 | totalNodes = len(isConnected) 15 | visited = [0] * totalNodes 16 | count = 0 17 | for i in range(totalNodes): 18 | if visited[i] == 0: 19 | self.dfs(i, isConnected, visited) 20 | count += 1 21 | return count 22 | -------------------------------------------------------------------------------- /16. Graphs/3. Questions/6. Cycle Detection in undirected Graph (dfs)/dfs.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | # Function to detect cycle in an undirected graph. 6 | def isCycle(self, V: int, adj: List[List[int]]) -> bool: 7 | visited = [0] * V 8 | 9 | def dfs(node, parent): 10 | visited[node] = 1 11 | for adjNode in adj[node]: 12 | if visited[adjNode] == 0: 13 | if dfs(adjNode, node): 14 | return True 15 | elif adjNode != parent: 16 | return True 17 | return False 18 | 19 | for i in range(V): 20 | if visited[i] == 0: 21 | if dfs(i, -1): 22 | return True 23 | return False 24 | -------------------------------------------------------------------------------- /16. Graphs/4. Topo Sort based problems/1. Topo Sort/dfs.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | 3 | def topologicalSort(self, adj): 4 | V = len(adj) 5 | visited = [False] * V 6 | order = [] 7 | 8 | def dfs(u): 9 | visited[u] = True 10 | for v in adj[u]: 11 | if not visited[v]: 12 | dfs(v) 13 | order.append(u) 14 | 15 | for i in range(V): 16 | if not visited[i]: 17 | dfs(i) 18 | return order[::-1] 19 | -------------------------------------------------------------------------------- /16. Graphs/4. Topo Sort based problems/1. Topo Sort/kahns-algorithm-bfs.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class Solution: 5 | def topologicalSort(self, adj): 6 | V = len(adj) 7 | indegree = [0] * V 8 | for u in range(V): 9 | for v in adj[u]: 10 | indegree[v] += 1 11 | q = deque() 12 | for i in range(V): 13 | if indegree[i] == 0: 14 | q.append(i) 15 | topo_order = [] 16 | while q: 17 | node = q.popleft() 18 | topo_order.append(node) 19 | for nxt in adj[node]: 20 | indegree[nxt] -= 1 21 | if indegree[nxt] == 0: 22 | q.append(nxt) 23 | return topo_order 24 | -------------------------------------------------------------------------------- /16. Graphs/4. Topo Sort based problems/2. Cycle Detection in Directed Graph (BFS)/optimal.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class Solution: 5 | def isCyclic(self, V, adj): 6 | 7 | indegree = [0] * V 8 | for i in range(V): 9 | for v in adj[i]: 10 | indegree[v] += 1 11 | q = deque() 12 | for i in range(V): 13 | if indegree[i] == 0: 14 | q.append(i) 15 | count = 0 16 | while q: 17 | u = q.popleft() 18 | count += 1 19 | for v in adj[u]: 20 | indegree[v] -= 1 21 | if indegree[v] == 0: 22 | q.append(v) 23 | return count < V # True if there's a cycle, False otherwise 24 | -------------------------------------------------------------------------------- /16. Graphs/4. Topo Sort based problems/3. Course Schedule - I/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: 6 | from collections import deque 7 | 8 | graph = [[] for _ in range(numCourses)] 9 | indegree = [0] * numCourses 10 | for course, pre in prerequisites: 11 | graph[pre].append(course) 12 | indegree[course] += 1 13 | q = deque() 14 | for i in range(numCourses): 15 | if indegree[i] == 0: 16 | q.append(i) 17 | completed = 0 18 | while q: 19 | node = q.popleft() 20 | completed += 1 21 | for nxt in graph[node]: 22 | indegree[nxt] -= 1 23 | if indegree[nxt] == 0: 24 | q.append(nxt) 25 | return completed == numCourses 26 | -------------------------------------------------------------------------------- /16. Graphs/4. Topo Sort based problems/4. Course Schedule - II/optimal.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | from typing import List 3 | 4 | 5 | class Solution: 6 | def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]: 7 | graph = [[] for _ in range(numCourses)] 8 | indegree = [0] * numCourses 9 | for course, pre in prerequisites: 10 | graph[pre].append(course) 11 | indegree[course] += 1 12 | q = deque() 13 | for i in range(numCourses): 14 | if indegree[i] == 0: 15 | q.append(i) 16 | order = [] 17 | while q: 18 | node = q.popleft() 19 | order.append(node) 20 | for nxt in graph[node]: 21 | indegree[nxt] -= 1 22 | if indegree[nxt] == 0: 23 | q.append(nxt) 24 | return order if len(order) == numCourses else [] 25 | -------------------------------------------------------------------------------- /17. Bit Manipulation/1. Introduction/1. number to binary.py: -------------------------------------------------------------------------------- 1 | def convert2Binary(num: int) -> str: 2 | result = "" 3 | while num > 0: 4 | if num % 2 == 1: 5 | result += "1" 6 | else: 7 | result += "0" 8 | num = num // 2 9 | return result[::-1] 10 | 11 | 12 | print(convert2Binary(13)) 13 | -------------------------------------------------------------------------------- /17. Bit Manipulation/1. Introduction/2. binary to decimal.py: -------------------------------------------------------------------------------- 1 | def binary_to_decimal(binary_string): 2 | decimal_number = 0 3 | power = 0 4 | 5 | index = len(binary_string) - 1 6 | 7 | while index >= 0: 8 | bit = int(binary_string[index]) 9 | decimal_number += bit * (2**power) 10 | power += 1 11 | index -= 1 12 | 13 | return decimal_number 14 | 15 | 16 | binary_string = "1101" 17 | decimal_representation = binary_to_decimal(binary_string) 18 | print(f"The decimal representation of {binary_string} is {decimal_representation}") 19 | -------------------------------------------------------------------------------- /17. Bit Manipulation/2. Basic Problems/1. Swap numbers.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def get(self, a, b): 3 | a = a ^ b 4 | b = a ^ b 5 | a = a ^ b 6 | return [a, b] 7 | -------------------------------------------------------------------------------- /17. Bit Manipulation/2. Basic Problems/2. Check if ith bit is set or not.py: -------------------------------------------------------------------------------- 1 | # Using LEFT shift 2 | class Solution: 3 | def checkKthBit(self, n, k): 4 | if n & (1 << k) != 0: 5 | return True 6 | return False 7 | 8 | 9 | # Using RIGHT shift 10 | class Solution: 11 | def checkKthBit(self, n, k): 12 | if (n >> k) & 1 == 1: 13 | return True 14 | return False 15 | -------------------------------------------------------------------------------- /17. Bit Manipulation/2. Basic Problems/3. Minimum number of flips to convert.py: -------------------------------------------------------------------------------- 1 | # Optimal 1 2 | class Solution: 3 | def minBitFlips(self, start: int, goal: int) -> int: 4 | ans = start ^ goal 5 | count = 0 6 | for i in range(0, 32): 7 | if ans & (1 << i) != 0: 8 | count += 1 9 | return count 10 | 11 | 12 | # Optimal 2 13 | class Solution: 14 | def minBitFlips(self, start: int, goal: int) -> int: 15 | ans = start ^ goal 16 | count = 0 17 | while ans > 0: 18 | if ans % 2 == 1: 19 | count += 1 20 | ans = ans // 2 21 | return count 22 | -------------------------------------------------------------------------------- /17. Bit Manipulation/2. Basic Problems/4. Single number.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | # Brute Force Solution 5 | class Solution: 6 | def singleNumber(self, nums: List[int]) -> int: 7 | n = len(nums) 8 | hash_map = dict() 9 | for i in range(n): 10 | hash_map[nums[i]] = hash_map.get(nums[i], 0) + 1 11 | 12 | for k in hash_map: 13 | if hash_map[k] == 1: 14 | return k 15 | 16 | 17 | # Optimal Solution 18 | class Solution: 19 | def singleNumber(self, nums: List[int]) -> int: 20 | ans = 0 21 | for i in range(0, len(nums)): 22 | ans = ans ^ nums[i] 23 | return ans 24 | -------------------------------------------------------------------------------- /17. Bit Manipulation/2. Basic Problems/5. Power-set.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def subsets(self, nums: List[int]) -> List[List[int]]: 6 | n = len(nums) 7 | total_subset = 1 << n 8 | result = [] 9 | for num in range(total_subset): 10 | lst = [] 11 | for i in range(0, n): 12 | if num & (1 << i) != 0: 13 | lst.append(nums[i]) 14 | result.append(lst) 15 | return result 16 | -------------------------------------------------------------------------------- /17. Bit Manipulation/2. Basic Problems/6. xor from left to right.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def XORfrom1ToN(self, num): 3 | if num % 4 == 1: 4 | return 1 5 | elif num % 4 == 2: 6 | return num + 1 7 | elif num % 4 == 3: 8 | return 0 9 | else: 10 | return num 11 | 12 | def findXOR(self, l, r): 13 | return self.XORfrom1ToN(l - 1) ^ self.XORfrom1ToN(r) 14 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/1. Easy/1. Assign Cookies/1. optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def findContentChildren(self, g: List[int], s: List[int]) -> int: 6 | n = len(g) 7 | m = len(s) 8 | g.sort() 9 | s.sort() 10 | left = 0 11 | right = 0 12 | count = 0 13 | while left < n and right < m: 14 | if g[left] <= s[right]: 15 | count += 1 16 | left += 1 17 | right += 1 18 | return count 19 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/1. Easy/2. Fractional Knapsack/1. optimal.py: -------------------------------------------------------------------------------- 1 | class Item: 2 | def __init__(self, val, w): 3 | self.value = val 4 | self.weight = w 5 | 6 | 7 | class Solution: 8 | def fractionalknapsack(self, w, arr, n): 9 | arr.sort(key=lambda x: x.value / x.weight, reverse=True) 10 | curWeight = 0 11 | finalvalue = 0.0 12 | for i in range(n): 13 | if curWeight + arr[i].weight <= w: 14 | curWeight += arr[i].weight 15 | finalvalue += arr[i].value 16 | else: 17 | remain = w - curWeight 18 | finalvalue += arr[i].value / arr[i].weight * remain 19 | break 20 | return finalvalue 21 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/1. Easy/4. Lemonade Change/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def lemonadeChange(self, bills: List[int]) -> bool: 6 | n = len(bills) 7 | five = 0 8 | ten = 0 9 | for i in range(0, n): 10 | if bills[i] == 5: 11 | five += 1 12 | elif bills[i] == 10: 13 | if five >= 1: 14 | five -= 1 15 | ten += 1 16 | else: 17 | return False 18 | else: 19 | if ten >= 1 and five >= 1: 20 | five -= 1 21 | ten -= 1 22 | elif five >= 3: 23 | five -= 3 24 | else: 25 | return False 26 | return True 27 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/2. Medium or Hard/1. N meetings in one room/optimal.py: -------------------------------------------------------------------------------- 1 | class Meeting: 2 | def __init__(self, start, end, position): 3 | self.start = start 4 | self.end = end 5 | self.position = position 6 | 7 | 8 | class Solution: 9 | def maximumMeetings(self, n, start, end): 10 | meets = [Meeting(start[i], end[i], i + 1) for i in range(n)] 11 | meets.sort(key=lambda x: (x.end, x.start)) 12 | lastTime = meets[0].end 13 | count = 1 14 | for i in range(1, n): 15 | if meets[i].start > lastTime: 16 | count += 1 17 | lastTime = meets[i].end 18 | return count 19 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/2. Medium or Hard/2. Jump Game/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity: 3 | The complexity is O(n), where n is the number of elements in nums. 4 | This is because the algorithm involves a single pass through the array. 5 | 6 | Space Complexity: 7 | The space complexity is O(1), as no additional space is used beyond variable storage. 8 | """ 9 | 10 | from typing import List 11 | 12 | 13 | class Solution: 14 | def canJump(self, nums: List[int]) -> bool: 15 | max_index = 0 16 | 17 | for i in range(len(nums)): 18 | if i > max_index: 19 | return False 20 | 21 | max_index = max(max_index, i + nums[i]) 22 | 23 | return True 24 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/2. Medium or Hard/3. Jump Game 2/1. recursion.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def solve(self, index, jump, lastIndex, nums): 6 | if index >= lastIndex: 7 | return jump 8 | minJump = float("inf") 9 | for i in range(1, nums[index] + 1): 10 | minJump = min(minJump, self.solve(index + i, jump + 1, lastIndex, nums)) 11 | return minJump 12 | 13 | def jump(self, nums: List[int]) -> int: 14 | return self.solve(0, 0, len(nums) - 1, nums) 15 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/2. Medium or Hard/3. Jump Game 2/2. memoization.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def solve(self, index, jump, lastIndex, nums, dp): 6 | if index >= lastIndex: 7 | return jump 8 | 9 | if dp[index][jump] != -1: 10 | return dp[index][jump] 11 | 12 | minJump = float("inf") 13 | for i in range(1, nums[index] + 1): 14 | minJump = min(minJump, self.solve(index + i, jump + 1, lastIndex, nums, dp)) 15 | 16 | dp[index][jump] = minJump 17 | return minJump 18 | 19 | def jump(self, nums: List[int]) -> int: 20 | n = len(nums) 21 | dp = [[-1] * n for _ in range(n)] # Initialize dp array with -1 22 | return self.solve(0, 0, n - 1, nums, dp) 23 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/2. Medium or Hard/3. Jump Game 2/3. tabulation.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def jump(self, nums: List[int]) -> int: 6 | n = len(nums) 7 | if n == 1: 8 | return 0 9 | 10 | # Initialize dp array where dp[i] represents the minimum jumps to reach the last index from index i 11 | dp = [float("inf")] * n 12 | dp[n - 1] = ( 13 | 0 # Base case: 0 jumps needed to reach the end if we're already there 14 | ) 15 | 16 | # Fill the dp array from the second last index to the first index 17 | for i in range(n - 2, -1, -1): 18 | furthest_jump = min(i + nums[i], n - 1) 19 | for j in range(i + 1, furthest_jump + 1): 20 | dp[i] = min(dp[i], 1 + dp[j]) 21 | 22 | return dp[0] 23 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/2. Medium or Hard/3. Jump Game 2/4. tabulation [space-optimize].py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def jump(self, nums: List[int]) -> int: 6 | n = len(nums) 7 | if n == 1: 8 | return 0 9 | 10 | jumps = 0 11 | current_end = 0 12 | farthest = 0 13 | 14 | for i in range(n - 1): 15 | farthest = max(farthest, i + nums[i]) 16 | 17 | # When we reach the end of the current jump range 18 | if i == current_end: 19 | jumps += 1 20 | current_end = farthest 21 | 22 | # Early exit if we can already reach the last index 23 | if current_end >= n - 1: 24 | break 25 | 26 | return jumps 27 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/2. Medium or Hard/3. Jump Game 2/5. greedy.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def jump(self, nums: List[int]) -> int: 6 | n = len(nums) 7 | jumps = 0 8 | left = 0 9 | right = 0 10 | while right < n - 1: 11 | farthest = 0 12 | for i in range(left, right + 1): 13 | farthest = max(farthest, i + nums[i]) 14 | left = right + 1 15 | right = farthest 16 | jumps += 1 17 | return jumps 18 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/2. Medium or Hard/4. Minimum Platforms/1. brute.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | # Function to find the minimum number of platforms required at the 3 | # railway station such that no train waits. 4 | def minimumPlatform(self, n, arr, dep): 5 | ans = 1 6 | for i in range(n): 7 | count = 1 8 | for j in range(i + 1, n): 9 | if (arr[i] >= arr[j] and arr[i] <= dep[j]) or ( 10 | arr[j] >= arr[i] and arr[j] <= dep[i] 11 | ): 12 | count += 1 13 | ans = max(ans, count) 14 | return ans 15 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/2. Medium or Hard/4. Minimum Platforms/2. optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | # Function to find the minimum number of platforms required at the 3 | # railway station such that no train waits. 4 | def minimumPlatform(self, n, arr, dep): 5 | arr.sort() 6 | dep.sort() 7 | 8 | ans = 1 9 | count = 1 10 | i = 1 11 | j = 0 12 | while i < len(arr) and j < len(dep): 13 | if arr[i] <= dep[j]: # one more platform needed 14 | count += 1 15 | i += 1 16 | else: # one platform can be reduced 17 | count -= 1 18 | j += 1 19 | ans = max(ans, count) 20 | return ans 21 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/2. Medium or Hard/5. Candy/1. brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def candy(self, ratings: List[int]) -> int: 6 | n = len(ratings) 7 | left = [-1] * n 8 | right = [-1] * n 9 | left[0] = 1 10 | right[-1] = 1 11 | curr_rating = 1 12 | for i in range(1, n): 13 | if ratings[i] > ratings[i - 1]: 14 | curr_rating += 1 15 | else: 16 | curr_rating = 1 17 | left[i] = curr_rating 18 | for i in range(n - 2, -1, -1): 19 | if ratings[i] > ratings[i + 1]: 20 | curr_rating += 1 21 | else: 22 | curr_rating = 1 23 | right[i] = curr_rating 24 | answer = 0 25 | for i in range(n): 26 | answer += max(left[i], right[i]) 27 | return answer 28 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/2. Medium or Hard/5. Candy/2. better.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def candy(self, ratings: List[int]) -> int: 6 | n = len(ratings) 7 | left = [-1] * n 8 | left[0] = 1 9 | curr_rating = 1 10 | for i in range(1, n): 11 | if ratings[i] > ratings[i - 1]: 12 | curr_rating += 1 13 | else: 14 | curr_rating = 1 15 | left[i] = curr_rating 16 | curr = 1 17 | right = 1 18 | total = max(1, left[n - 1]) 19 | for i in range(n - 2, -1, -1): 20 | if ratings[i] > ratings[i + 1]: 21 | curr = right + 1 22 | else: 23 | curr = 1 24 | total = total + max(left[i], curr) 25 | right = curr 26 | return total 27 | -------------------------------------------------------------------------------- /18. Greedy Algorithms/2. Medium or Hard/5. Candy/3. optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def candy(self, ratings: List[int]) -> int: 6 | n = len(ratings) 7 | total = 1 8 | i = 1 9 | while i < n: 10 | if ratings[i] == ratings[i - 1]: 11 | total += 1 12 | i += 1 13 | continue 14 | peak = 1 15 | while i < n and ratings[i] > ratings[i - 1]: 16 | peak += 1 17 | total += peak 18 | i += 1 19 | down = 1 20 | while i < n and ratings[i] < ratings[i - 1]: 21 | total += down 22 | down += 1 23 | i += 1 24 | if down > peak: 25 | total += down - peak 26 | return total 27 | 28 | 29 | obj = Solution() 30 | print(obj.candy([1, 3, 5, 5, 3, 2, 1, 2, 3, 4])) 31 | -------------------------------------------------------------------------------- /2. Recursion/basics.py: -------------------------------------------------------------------------------- 1 | count = 1 2 | 3 | 4 | def func(): 5 | global count 6 | print(count) 7 | if count == 5: 8 | return 9 | count += 1 10 | func() 11 | 12 | 13 | if __name__ == "__main__": 14 | func() 15 | -------------------------------------------------------------------------------- /2. Recursion/factorial.py: -------------------------------------------------------------------------------- 1 | """ 2 | Calculate factorial of a number 3 | using recursion 4 | """ 5 | 6 | 7 | # Functional way 8 | def factorial(n): 9 | if n == 0 or n == 1: 10 | return 1 11 | 12 | return n * factorial(n - 1) 13 | 14 | 15 | # Parameterized way 16 | def factorial1(i, sum): 17 | if i < 1: 18 | print(sum) 19 | return 20 | factorial1(i - 1, sum * i) 21 | 22 | 23 | if __name__ == "__main__": 24 | print(factorial(5)) 25 | factorial1(5, 1) 26 | -------------------------------------------------------------------------------- /2. Recursion/fibonacci.py: -------------------------------------------------------------------------------- 1 | def fib(n): 2 | if n == 1: 3 | return 1 4 | elif n == 0: 5 | return 0 6 | return fib(n - 1) + fib(n - 2) 7 | 8 | 9 | print(fib(7)) 10 | print(fib(9)) 11 | -------------------------------------------------------------------------------- /2. Recursion/palindrome.py: -------------------------------------------------------------------------------- 1 | """ 2 | Check if a string is palindrome 3 | using recursion 4 | """ 5 | 6 | 7 | def checkPalindrome(string, i): 8 | if i >= len(string) // 2: 9 | return True 10 | if string[i] != string[len(string) - i - 1]: 11 | return False 12 | return checkPalindrome(string, i + 1) 13 | 14 | 15 | if __name__ == "__main__": 16 | result = checkPalindrome("momm", 0) 17 | print(result) 18 | -------------------------------------------------------------------------------- /2. Recursion/power.py: -------------------------------------------------------------------------------- 1 | def power(b, e): 2 | if type(e) != int or e < 0: 3 | raise Exception("Invalid e value") 4 | if e == 0: 5 | return 1 6 | elif e == 1: 7 | return b 8 | return b * power(b, e - 1) 9 | 10 | 11 | print(power(5, 3)) 12 | print(power(5, 1)) 13 | print(power(5, 0)) 14 | print(power(5, 2.5)) 15 | -------------------------------------------------------------------------------- /2. Recursion/print_1_to_n.py: -------------------------------------------------------------------------------- 1 | """ 2 | Print numbers from 1 to N 3 | """ 4 | 5 | 6 | # Without backtracking 7 | def func(i, n): 8 | if i > n: 9 | return 10 | print(i) 11 | func(i + 1, n) 12 | 13 | 14 | # With backtracking 15 | def func2(i, n): 16 | if i < 1: 17 | return 18 | func2(i - 1, n) 19 | print(i) 20 | 21 | 22 | if __name__ == "__main__": 23 | n = 5 24 | # func(1, n) 25 | func2(n, n) 26 | -------------------------------------------------------------------------------- /2. Recursion/print_n_times.py: -------------------------------------------------------------------------------- 1 | """ 2 | Print name N times 3 | 4 | TC -> O(N) 5 | SC -> O(N) 6 | """ 7 | 8 | 9 | def func(i: int, n: int): 10 | if i > n: 11 | return 12 | print("Code and Debug") 13 | func(i + 1, n) 14 | 15 | 16 | if __name__ == "__main__": 17 | func(1, 5) 18 | -------------------------------------------------------------------------------- /2. Recursion/print_n_to_1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Back-tracking solution to print n to 1 without minus (-) 3 | """ 4 | 5 | 6 | def func(i, n): 7 | if i > n: 8 | return 9 | func(i + 1, n) 10 | print(i) 11 | 12 | 13 | func(1, 4) 14 | -------------------------------------------------------------------------------- /2. Recursion/reverse_an_array.py: -------------------------------------------------------------------------------- 1 | def reverse_list(lst, start, end): 2 | if start > end: 3 | return 4 | lst[start], lst[end] = lst[end], lst[start] 5 | reverse_list(lst, start + 1, end - 1) 6 | 7 | 8 | # Alternate Method using just one Index 9 | def revAr(arr, ind): 10 | n = len(arr) 11 | if ind == n // 2: 12 | print(arr) 13 | return 14 | arr[ind], arr[n - 1 - ind] = arr[n - 1 - ind], arr[ind] 15 | revAr(arr, ind + 1) 16 | 17 | 18 | if __name__ == "__main__": 19 | my_list = [3, 5, 1, 7, 8, 6, 5, 100] 20 | reverse_list(my_list, 0, len(my_list) - 1) 21 | print(my_list) 22 | 23 | new_list = [3, 5, 1, 7, 8, 6, 5, 100] 24 | revAr(new_list, 0) 25 | -------------------------------------------------------------------------------- /2. Recursion/sum_of_1_to_n.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sum of numbers from 1 to N 3 | """ 4 | 5 | 6 | # Parameterized Way 7 | def add(i, sum): 8 | if i < 1: 9 | print(sum) 10 | return 11 | add(i - 1, sum + i) 12 | 13 | 14 | # Functional Way 15 | def addition(n): 16 | if n == 1: 17 | return 1 18 | return n + addition(n - 1) 19 | 20 | 21 | # add(5, 0) 22 | x = addition(5) 23 | print(x) 24 | -------------------------------------------------------------------------------- /20. Strings/1. Easy/1. Leetcode 1021 - Remove Outermost Parentheses/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def removeOuterParentheses(self, s: str) -> str: 3 | result = "" 4 | count = 0 5 | for ch in s: 6 | if ch == "(": 7 | count += 1 8 | if count > 1: 9 | result += ch 10 | else: 11 | count -= 1 12 | if count > 0: 13 | result += ch 14 | return result 15 | -------------------------------------------------------------------------------- /20. Strings/1. Easy/2. Leetcode 151 - Reverse Words in a String/brute-force.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def reverseWords(self, s: str) -> str: 3 | words = s.split() 4 | words.reverse() 5 | result = " ".join(words) 6 | return result 7 | -------------------------------------------------------------------------------- /20. Strings/1. Easy/2. Leetcode 151 - Reverse Words in a String/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def reverseWords(self, s: str) -> str: 3 | result = "" 4 | i = len(s) - 1 5 | while i >= 0: 6 | # Skip any trailing spaces 7 | if s[i] == " ": 8 | i -= 1 9 | continue 10 | # Collect the characters of the current word 11 | word = "" 12 | while i >= 0 and s[i] != " ": 13 | word = s[i] + word 14 | i -= 1 15 | # Add the word to the result string 16 | if result != "": 17 | result += " " 18 | result += word 19 | return result 20 | -------------------------------------------------------------------------------- /20. Strings/1. Easy/3. Leetcode 1903 - Largest Odd Number in String/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def largestOddNumber(self, num: str) -> str: 3 | n = len(num) 4 | for i in range(n - 1, -1, -1): 5 | if int(num[i]) % 2 == 1: 6 | return num[: i + 1] 7 | return "" 8 | -------------------------------------------------------------------------------- /20. Strings/1. Easy/4. Leetcode 14 - Longest Common Prefix/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def longestCommonPrefix(self, strs): 3 | if len(strs) == 0: 4 | return "" 5 | result = "" 6 | base = strs[0] 7 | for i in range(len(base)): 8 | for word in strs[1:]: 9 | if i == len(word) or word[i] != base[i]: 10 | return result 11 | result += base[i] 12 | -------------------------------------------------------------------------------- /20. Strings/1. Easy/6. Leetcode 796 - Rotate String/brute-force.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def rotateString(self, s: str, goal: str) -> bool: 3 | n = len(s) 4 | current_string = s 5 | for i in range(n): 6 | if current_string == goal: 7 | return True 8 | current_string = current_string[-1] + current_string[:-1] 9 | return False 10 | -------------------------------------------------------------------------------- /20. Strings/1. Easy/6. Leetcode 796 - Rotate String/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def rotateString(self, s: str, goal: str) -> bool: 3 | # Check if the lengths of s and goal are the same 4 | if len(s) != len(goal): 5 | return False 6 | 7 | # Concatenate s with itself 8 | s_double = s + s 9 | 10 | # Check if goal is a substring of s_double 11 | return goal in s_double 12 | -------------------------------------------------------------------------------- /20. Strings/1. Easy/7. Leetcode 242 - Valid Anagram/brute-force.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isAnagram(self, s: str, t: str) -> bool: 3 | if len(s) != len(t): 4 | return False 5 | sorted_s = sorted(s) 6 | sorted_t = sorted(t) 7 | if sorted_s == sorted_t: 8 | return True 9 | return False 10 | -------------------------------------------------------------------------------- /20. Strings/1. Easy/7. Leetcode 242 - Valid Anagram/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isAnagram(self, s: str, t: str) -> bool: 3 | if len(s) != len(t): 4 | return False 5 | chars = {} 6 | for ch in s: 7 | chars[ch] = chars.get(ch, 0) + 1 8 | for ch in t: 9 | if ch not in chars: 10 | return False 11 | else: 12 | if chars[ch] == 0: 13 | return False 14 | chars[ch] -= 1 15 | return True 16 | -------------------------------------------------------------------------------- /21. Binary Search Trees/1. Introduciton/1. Search in a Binary Search Tree/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | # Definition for a binary tree node. 5 | class TreeNode: 6 | def __init__(self, val=0, left=None, right=None): 7 | self.val = val 8 | self.left = left 9 | self.right = right 10 | 11 | 12 | class Solution: 13 | def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: 14 | temp = root 15 | while temp is not None: 16 | if temp.val == val: 17 | return temp 18 | elif val < temp.val: 19 | temp = temp.left 20 | else: 21 | temp = temp.right 22 | return None 23 | -------------------------------------------------------------------------------- /21. Binary Search Trees/1. Introduciton/2. Find Min or Max in BST/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | class Node: 3 | def __init__(self, val): 4 | self.right = None 5 | self.data = val 6 | self.left = None 7 | """ 8 | 9 | 10 | class Solution: 11 | # Function to find the minimum element in the given BST. 12 | def minValue(self, root): 13 | while root and root.left is not None: 14 | root = root.left 15 | return root.data 16 | -------------------------------------------------------------------------------- /21. Binary Search Trees/2. Practice Problems/1. Ceil in a Binary Search Tree/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findCeil(self, root, inp): 3 | ceil = -1 4 | while root is not None: 5 | if root.key == inp: 6 | return root.key 7 | elif inp < root.key: 8 | ceil = root.key 9 | root = root.left 10 | else: 11 | root = root.right 12 | return ceil 13 | -------------------------------------------------------------------------------- /3. Hashing/character-hashing.py: -------------------------------------------------------------------------------- 1 | string = "aerrropllane" 2 | 3 | char_hash = [0] * 26 4 | 5 | # Pre store 6 | for ch in string: 7 | index = ord(ch) - 97 8 | char_hash[index] += 1 9 | 10 | print(char_hash) 11 | 12 | char = input("Enter char to count = ") 13 | print(char_hash[ord(char) - 97]) 14 | -------------------------------------------------------------------------------- /3. Hashing/dictionary(numbers).py: -------------------------------------------------------------------------------- 1 | lst = [5, 4, 2, 5, 4, 2, 1, 1, 1, 3, 5, 3] 2 | 3 | hash_map = {} 4 | 5 | # Pre compute 6 | for num in lst: 7 | hash_map[num] = hash_map.get(num, 0) + 1 8 | 9 | print(hash_map) 10 | 11 | number = int(input("Enter number to count = ")) 12 | print(hash_map.get(number, 0)) 13 | -------------------------------------------------------------------------------- /3. Hashing/dictionary(string).py: -------------------------------------------------------------------------------- 1 | string = "aeroplllanneee" 2 | 3 | hash_map = {} 4 | 5 | # Pre compute 6 | for num in string: 7 | hash_map[num] = hash_map.get(num, 0) + 1 8 | 9 | print(hash_map) 10 | 11 | number = input("Enter char to count = ") 12 | print(hash_map.get(number, 0)) 13 | -------------------------------------------------------------------------------- /3. Hashing/number-hashing.py: -------------------------------------------------------------------------------- 1 | lst = [] 2 | list_length = int(input("Enter length = ")) 3 | for i in range(list_length): 4 | num = int(input("Enter number = ")) 5 | lst.append(num) 6 | print(lst) 7 | 8 | # Pre compute 9 | hash_list = [0] * 13 # [0,0,0,0,0,0..13 times] 10 | for num in lst: 11 | hash_list[num] += 1 12 | 13 | print(hash_list) 14 | n = int(input("Enter number to count = ")) # 1 15 | print(hash_list[n]) # Fetch 16 | -------------------------------------------------------------------------------- /3. Hashing/question1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Question link 3 | 4 | https://www.codingninjas.com/studio/problems/count-frequency-in-a-range_8365446 5 | """ 6 | 7 | from typing import List 8 | 9 | 10 | def countFrequency(n: int, m: int, edges: List[List[int]]): 11 | 12 | mylst = [0] * n 13 | 14 | for x in edges: 15 | if x > n: 16 | continue 17 | mylst[x - 1] += 1 18 | return mylst 19 | 20 | 21 | print(countFrequency(6, 9, [1, 3, 1, 9, 2, 7])) 22 | -------------------------------------------------------------------------------- /4. Sorting/Coding Ninjas Solution/bubble-sort.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | def bubbleSort(arr: List[int], n: int): 5 | n = len(arr) 6 | # Traverse through all array elements in reverse order 7 | for i in range(n - 2, -1, -1): 8 | # Last i elements are already in place 9 | for j in range(0, i + 1): 10 | # Traverse the array from 0 to i 11 | # Swap if the element found is greater than the next element 12 | if arr[j] > arr[j + 1]: 13 | arr[j], arr[j + 1] = arr[j + 1], arr[j] 14 | -------------------------------------------------------------------------------- /4. Sorting/Coding Ninjas Solution/insertion-sort.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from copy import deepcopy 4 | 5 | input = lambda: sys.stdin.readline().rstrip("\r\n") 6 | sys.setrecursionlimit(10**7) 7 | 8 | 9 | def insertionSort(arr): 10 | for i in range(1, len(arr)): 11 | key = arr[i] 12 | j = i - 1 13 | while j >= 0 and arr[j] > key: 14 | arr[j + 1] = arr[j] 15 | j -= 1 16 | arr[j + 1] = key 17 | 18 | 19 | class Runner: 20 | def __init__(self): 21 | self.n = 0 22 | self.arr = [] 23 | 24 | def takeInput(self): 25 | self.n = int(input()) 26 | self.arr = list(map(int, input().split())) 27 | 28 | def execute(self): 29 | 30 | ans = insertionSort(self.arr) 31 | 32 | def executeAndPrintOutput(self): 33 | ans = insertionSort(self.arr) 34 | print(*self.arr) 35 | 36 | 37 | runner = Runner() 38 | runner.takeInput() 39 | runner.executeAndPrintOutput() 40 | -------------------------------------------------------------------------------- /4. Sorting/Coding Ninjas Solution/selection-sort.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | def selectionSort(arr: List[int]) -> None: 5 | n = len(arr) 6 | 7 | # Traverse through all array elements 8 | for i in range(0, n): 9 | # Find the minimum element in the remaining unsorted array 10 | min_idx = i 11 | for j in range(i + 1, n): 12 | if arr[j] < arr[min_idx]: 13 | min_idx = j 14 | 15 | # Swap the found minimum element with the first element 16 | arr[i], arr[min_idx] = arr[min_idx], arr[i] 17 | -------------------------------------------------------------------------------- /4. Sorting/insertion-sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Insertion sort 3 | Time complexity - O(n^2) (Worst and Average) 4 | Time complexity - O(n) (Best case) 5 | Space complexity - O(1) 6 | """ 7 | 8 | 9 | def insertion_sort(arr): 10 | for i in range(1, len(arr)): 11 | key = arr[i] 12 | j = i - 1 13 | while j >= 0 and arr[j] > key: 14 | arr[j + 1] = arr[j] 15 | j -= 1 16 | arr[j + 1] = key 17 | 18 | 19 | arr = [5, 2, 4, 6, 1, 3] 20 | insertion_sort(arr) 21 | print(arr) 22 | -------------------------------------------------------------------------------- /4. Sorting/quick-sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity -> O(n log n) 3 | Time Complexity -> O(n^2) (Worst Case) 4 | Space Complexity -> O(1) 5 | """ 6 | 7 | 8 | def partition(arr, low, high): 9 | pivot = arr[low] 10 | i = low 11 | j = high 12 | 13 | while i < j: 14 | while arr[i] <= pivot and i <= high - 1: 15 | i += 1 16 | 17 | while arr[j] > pivot and j >= low + 1: 18 | j -= 1 19 | 20 | if i < j: 21 | arr[i], arr[j] = arr[j], arr[i] 22 | 23 | arr[low], arr[j] = arr[j], arr[low] 24 | return j 25 | 26 | 27 | def quick_sort(arr, low, high): 28 | if low < high: 29 | p_index = partition(arr, low, high) 30 | quick_sort(arr, low, p_index - 1) 31 | quick_sort(arr, p_index + 1, high) 32 | 33 | 34 | arr = [64, 34, 25, 12, 22, 12, 12, 11, 90] 35 | quick_sort(arr, 0, len(arr) - 1) 36 | print(f"Sorted array = {arr}") 37 | -------------------------------------------------------------------------------- /4. Sorting/selection-sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Selection sort 3 | Time complexity - O(n^2) 4 | Space complexity - O(1) 5 | """ 6 | 7 | 8 | def selection_sort(arr): 9 | n = len(arr) 10 | 11 | # Traverse through all array elements 12 | for i in range(0, n): 13 | # Find the minimum element in the remaining unsorted array 14 | min_idx = i 15 | for j in range(i + 1, n): 16 | if arr[j] < arr[min_idx]: 17 | min_idx = j 18 | 19 | # Swap the found minimum element with the first element 20 | arr[i], arr[min_idx] = arr[min_idx], arr[i] 21 | 22 | 23 | arr = [64, 25, 12, 22, 11] 24 | selection_sort(arr) 25 | print(arr) 26 | -------------------------------------------------------------------------------- /5. Lists/Easy/1. Largest element in the array/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity = O(N logN) where N is the number of elements in list 3 | Space complexity = O(1) 4 | """ 5 | 6 | from sys import * 7 | from collections import * 8 | from math import * 9 | 10 | 11 | def largestElement(arr: [], n: int) -> int: 12 | # Sort the elements first 13 | arr.sort() 14 | 15 | # Return the largest from the end 16 | return arr[-1] 17 | -------------------------------------------------------------------------------- /5. Lists/Easy/1. Largest element in the array/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity = O(N) where N is the number of elements in list 3 | Space complexity = O(1) 4 | """ 5 | 6 | from sys import * 7 | from collections import * 8 | from math import * 9 | 10 | 11 | def largestElement(arr: [], n: int) -> int: 12 | max_num = arr[0] 13 | for num in arr: 14 | if num > max_num: 15 | max_num = num 16 | return max_num 17 | -------------------------------------------------------------------------------- /5. Lists/Easy/10. Merge 2 Sorted Array/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O((n + m) log (n + m)) 3 | n is number of elements in a 4 | m is number of elements in b 5 | 6 | Space complexity -> O(n+m) 7 | """ 8 | 9 | 10 | def sortedArray(a: [int], b: [int]) -> [int]: 11 | freq = set() 12 | 13 | for num in a: 14 | freq.add(num) 15 | for num in b: 16 | freq.add(num) 17 | 18 | return sorted(list(freq)) 19 | -------------------------------------------------------------------------------- /5. Lists/Easy/10. Merge 2 Sorted Array/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(n+m) 3 | n is number of elements in a 4 | m is number of elements in b 5 | 6 | Space complexity -> O(1) 7 | """ 8 | 9 | 10 | def sortedArray(a: [int], b: [int]) -> [int]: 11 | i = 0 12 | j = 0 13 | result = [] 14 | while i < len(a) and j < len(b): 15 | if a[i] <= b[j]: 16 | if len(result) == 0 or result[-1] != a[i]: 17 | result.append(a[i]) 18 | i += 1 19 | else: 20 | if len(result) == 0 or result[-1] != b[j]: 21 | result.append(b[j]) 22 | j += 1 23 | 24 | while i < len(a): 25 | if len(result) == 0 or result[-1] != a[i]: 26 | result.append(a[i]) 27 | i += 1 28 | 29 | while j < len(b): 30 | if len(result) == 0 or result[-1] != b[j]: 31 | result.append(b[j]) 32 | j += 1 33 | 34 | return result 35 | -------------------------------------------------------------------------------- /5. Lists/Easy/11. Leetcode 268 – Missing Number/better.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(n) 8 | """ 9 | 10 | 11 | class Solution: 12 | def missingNumber(self, nums: List[int]) -> int: 13 | freq = {i: 0 for i in range(0, len(nums) + 1)} 14 | for i in nums: 15 | freq[i] += 1 16 | for k, v in freq.items(): 17 | if v == 0: 18 | return k 19 | -------------------------------------------------------------------------------- /5. Lists/Easy/11. Leetcode 268 – Missing Number/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n^2) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def missingNumber(self, nums: List[int]) -> int: 13 | for i in range(0, len(nums) + 1): 14 | if i not in nums: 15 | return i 16 | -------------------------------------------------------------------------------- /5. Lists/Easy/11. Leetcode 268 – Missing Number/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def missingNumber(self, nums: List[int]) -> int: 13 | n = len(nums) 14 | original_total = (n * (n + 1)) // 2 15 | return original_total - sum(nums) 16 | -------------------------------------------------------------------------------- /5. Lists/Easy/12. Leetcode 485 - Max Consecutive Ones/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def findMaxConsecutiveOnes(self, nums: List[int]) -> int: 13 | cnt = 0 14 | maxi = 0 15 | for i in range(len(nums)): 16 | if nums[i] == 1: 17 | cnt += 1 18 | else: 19 | maxi = max(maxi, cnt) 20 | cnt = 0 21 | return max(maxi, cnt) 22 | -------------------------------------------------------------------------------- /5. Lists/Easy/2. Second Largest Number/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity = O(N logN) where N is the number of elements in list 3 | Space complexity = O(1) 4 | """ 5 | 6 | # This method will work only if there are no duplicates in the array 7 | from typing import List 8 | 9 | 10 | def getSecondOrderElements(n: int, a: List[int]) -> List[int]: 11 | # Sort the array 12 | a.sort() 13 | 14 | # Second minimum will be on 1st index 15 | # Second largest will be on last second index 16 | return [a[-2], a[1]] 17 | -------------------------------------------------------------------------------- /5. Lists/Easy/3. Check Sorted Array/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity = O(N) where N is the number of elements in list 3 | Because we looping through the array only once 4 | 5 | Space complexity = O(1) 6 | """ 7 | 8 | from typing import List 9 | 10 | 11 | def isSorted(n: int, a: List[int]) -> int: 12 | for i in range(0, len(a) - 1): 13 | if a[i] > a[i + 1]: 14 | return 0 15 | return 1 16 | -------------------------------------------------------------------------------- /5. Lists/Easy/4. Leetcodee - 1752 - Check if Array Is Sorted and Rotated/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity = O(N) where N is the number of elements in list 5 | Because we looping through the array only once 6 | 7 | Space complexity = O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def check(self, nums: List[int]) -> bool: 13 | n = len(nums) 14 | 15 | # We will count the rotations, we know the array can rotated only 1 time 16 | # and can be sorted, if the rotation will be greater than 1, we know the 17 | # array is not sorted and this return false 18 | rotations = 0 19 | for i in range(0, len(nums)): 20 | if nums[i] > nums[(i + 1) % n]: 21 | rotations += 1 22 | if rotations > 1: 23 | return False 24 | return True 25 | -------------------------------------------------------------------------------- /5. Lists/Easy/5. Leetcode - 26 -Remove Duplicates from Sorted Array/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity = O(n) where n is the number of elements in list 5 | We are looping two different times, so it will be O(n) + O(n). 6 | Which equals tos O(n) 7 | 8 | Space complexity = O(n), suppose all numbers are unique, it will take same length as list 9 | """ 10 | 11 | 12 | class Solution: 13 | def removeDuplicates(self, nums: List[int]) -> int: 14 | my_dict = dict() 15 | for i in nums: 16 | my_dict[i] = 0 17 | 18 | j = 0 19 | for n in my_dict: 20 | nums[j] = n 21 | j += 1 22 | return j 23 | -------------------------------------------------------------------------------- /5. Lists/Easy/5. Leetcode - 26 -Remove Duplicates from Sorted Array/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity = O(n) where n is the number of elements in list 5 | But in this case, we are using only 1 loop instead of 2 FOR loops 6 | 7 | Space complexity = O(1), no extra space 8 | """ 9 | 10 | 11 | class Solution: 12 | def removeDuplicates(self, nums: List[int]) -> int: 13 | if len(nums) == 1: 14 | return 1 15 | i = 0 16 | j = i + 1 17 | while j < len(nums): 18 | if nums[j] != nums[i]: 19 | i += 1 20 | nums[j], nums[i] = nums[i], nums[j] 21 | j += 1 22 | return i + 1 23 | -------------------------------------------------------------------------------- /5. Lists/Easy/6. Left Rotate array by one/brute-force.py: -------------------------------------------------------------------------------- 1 | from sys import * 2 | from collections import * 3 | from math import * 4 | 5 | """ 6 | Time Complexity = O(n), n is number of elements in array 7 | Space Complexity = O(1) 8 | """ 9 | 10 | 11 | def rotateArray(arr: [], n: int) -> []: 12 | last_element = arr.pop(0) 13 | arr.append(last_element) 14 | return arr 15 | -------------------------------------------------------------------------------- /5. Lists/Easy/6. Left Rotate array by one/optimal.py: -------------------------------------------------------------------------------- 1 | from sys import * 2 | from collections import * 3 | from math import * 4 | 5 | """ 6 | Time Complexity = O(n), n is number of elements in array 7 | Space Complexity = O(1) 8 | 9 | This code is actually similar to Brute force one 10 | """ 11 | 12 | 13 | def rotateArray(arr: [], n: int) -> []: 14 | temp = arr[0] # storing the first element of the array in a variable 15 | for i in range(n - 1): 16 | arr[i] = arr[i + 1] 17 | arr[n - 1] = temp # assign the value of the variable at the last index 18 | return arr 19 | 20 | #Rotating using Slicing 21 | #got "You are better than 100%" using the below 22 | """ 23 | def rotateArray(arr: [], n: int) -> []: 24 | # Write your code from here. 25 | 26 | return arr[1:] + [arr[0]] 27 | """ -------------------------------------------------------------------------------- /5. Lists/Easy/7. Leetcode 189 - Rotate Array/better.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time Complexity = O(n) 5 | 6 | Space Complexity = O(n) 7 | This space complexity arises because you are creating a new list 8 | of size n-k (nums[n-k:]) and another list of size k (nums[:n-k]) 9 | during the rotation operation. 10 | Although the rotation is performed in place, slicing operations 11 | create new lists, which consume additional memory proportional to the 12 | size of the original list. Therefore, the overall space complexity is O(n). 13 | """ 14 | 15 | 16 | class Solution: 17 | def rotate(self, nums: List[int], k: int) -> None: 18 | """ 19 | Do not return anything, modify nums in-place instead. 20 | """ 21 | n = len(nums) 22 | k %= n 23 | 24 | # Rotate the list inplace 25 | nums[:] = nums[n - k :] + nums[: n - k] 26 | -------------------------------------------------------------------------------- /5. Lists/Easy/7. Leetcode 189 - Rotate Array/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity = O(r*n), where r is number of rotations 5 | and n is number of elements in the array 6 | 7 | Space complexity = O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def rotate(self, nums: List[int], k: int) -> None: 13 | """ 14 | Do not return anything, modify nums in-place instead. 15 | """ 16 | # Becuase if k = 8 and length = 7, means 17 | # we should only rotate 1 time instead of 8 times 18 | rotations = k % len(nums) 19 | 20 | for _ in range(rotations): 21 | last = nums.pop() 22 | nums.insert(0, last) 23 | -------------------------------------------------------------------------------- /5. Lists/Easy/7. Leetcode 189 - Rotate Array/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time Complexity = O(n) 5 | Space Complexity = O(1) 6 | """ 7 | 8 | 9 | class Solution: 10 | def rotate(self, nums: List[int], k: int) -> None: 11 | """ 12 | Do not return anything, modify nums in-place instead. 13 | """ 14 | 15 | def reverse(l, r): 16 | while l < r: 17 | nums[l], nums[r] = nums[r], nums[l] 18 | l += 1 19 | r -= 1 20 | 21 | n = len(nums) 22 | k %= n # Handle cases where k > n 23 | 24 | reverse(n - k, n - 1) # Reverse the last k element 25 | reverse(0, n - k - 1) # Reverse the remaining elements 26 | reverse(0, n - 1) # Reverse the entire array 27 | -------------------------------------------------------------------------------- /5. Lists/Easy/8. Leetcode 283 - Move Zeroes/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time Complexity = O(N), N is length of array 5 | Space Complexity: O(N) 6 | """ 7 | 8 | 9 | class Solution: 10 | def moveZeroes(self, nums: List[int]) -> None: 11 | n = len(nums) 12 | temp = [] 13 | 14 | # Copy non-zero elements from original to temp array 15 | for i in range(n): 16 | if nums[i] != 0: 17 | temp.append(nums[i]) 18 | 19 | # Number of non-zero elements 20 | nz = len(temp) 21 | 22 | # Copy elements from temp to fill first nz fields of original array 23 | for i in range(nz): 24 | nums[i] = temp[i] 25 | 26 | # Fill the rest of the cells with 0 27 | for i in range(nz, n): 28 | nums[i] = 0 29 | -------------------------------------------------------------------------------- /5. Lists/Easy/8. Leetcode 283 - Move Zeroes/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def moveZeroes(self, nums: List[int]) -> None: 6 | if len(nums) == 1: 7 | return 8 | i = 0 9 | while i < len(nums): 10 | if nums[i] == 0: 11 | break 12 | i += 1 13 | if i == len(nums): 14 | return 15 | j = i + 1 16 | while j < len(nums): 17 | if nums[j] != 0: 18 | nums[i], nums[j] = nums[j], nums[i] 19 | i += 1 20 | j += 1 21 | -------------------------------------------------------------------------------- /5. Lists/Easy/9. Linear Seach/optimal.py: -------------------------------------------------------------------------------- 1 | #This is a linear search code with time complexity o(n) 2 | # :') 3 | 4 | def linearSearch(n: int, num: int, arr: [int]) -> int: 5 | for i in range(0, len(arr)): 6 | if arr[i] == num: 7 | return i 8 | return -1 9 | -------------------------------------------------------------------------------- /5. Lists/Hard/Count Inversions/brute-force.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def inversionCount(self, arr): 3 | n = len(arr) 4 | count = 0 5 | for i in range(0, n): 6 | for j in range(i + 1, n): 7 | if arr[i] > arr[j]: 8 | count += 1 9 | return count 10 | -------------------------------------------------------------------------------- /5. Lists/Hard/Find the repeating and missing numbers/better.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findTwoElement(self, arr, n): 3 | repeating, missing = -1, -1 4 | 5 | hash_list = [0] * (n + 1) 6 | for num in arr: 7 | hash_list[num] += 1 8 | 9 | for i in range(1, len(hash_list)): 10 | if hash_list[i] == 2: 11 | repeating = i 12 | elif hash_list[i] == 0: 13 | missing = i 14 | if repeating != -1 and missing != -1: 15 | return [repeating, missing] 16 | -------------------------------------------------------------------------------- /5. Lists/Hard/Find the repeating and missing numbers/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time Complexity 3 | O(N^2), where N = size of the given array. 4 | We are using nested loops to count occurrences of every element between 1 to N. 5 | 6 | Space Complexity 7 | O(1) as we are not using any extra space. 8 | """ 9 | 10 | 11 | class Solution: 12 | def findTwoElement(self, arr, n): 13 | repeating, missing = -1, -1 14 | 15 | for i in range(1, n + 1): 16 | count = 0 17 | for j in range(n): 18 | if arr[j] == i: 19 | count += 1 20 | 21 | if count == 2: 22 | repeating = i 23 | elif count == 0: 24 | missing = i 25 | 26 | if repeating != -1 and missing != -1: 27 | break 28 | 29 | return [repeating, missing] 30 | -------------------------------------------------------------------------------- /5. Lists/Hard/Find the repeating and missing numbers/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findTwoElement(self, arr, n): 3 | sN = (n * (n + 1)) // 2 4 | s2N = (n * (n + 1) * (2 * n + 1)) // 6 5 | 6 | s = 0 7 | s2 = 0 8 | for num in arr: 9 | s += num 10 | s2 += num**2 11 | 12 | val1 = s - sN # x-y 13 | val2 = s2 - s2N # x^2 - y^2 14 | 15 | # This is x+y 16 | val2 = val2 // val1 # (x^2 - y^2)// x-y 17 | x = (val1 + val2) // 2 18 | y = x - val1 19 | 20 | return [x, y] 21 | -------------------------------------------------------------------------------- /5. Lists/Hard/Leetcode 15 - 3Sum/better.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def threeSum(self, nums: List[int]) -> List[List[int]]: 6 | n = len(nums) 7 | result = set() 8 | for i in range(n): 9 | hashset = set() 10 | for j in range(i + 1, n): 11 | third = -(nums[i] + nums[j]) 12 | if third in hashset: 13 | temp = [nums[i], nums[j], third] 14 | temp.sort() 15 | result.add(tuple(temp)) 16 | hashset.add(nums[j]) 17 | 18 | ans = list(result) 19 | return ans 20 | -------------------------------------------------------------------------------- /5. Lists/Hard/Leetcode 15 - 3Sum/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def threeSum(self, nums: List[int]) -> List[List[int]]: 6 | my_set = set() 7 | n = len(nums) 8 | 9 | # check all possible triplets 10 | for i in range(n): 11 | for j in range(i + 1, n): 12 | for k in range(j + 1, n): 13 | if nums[i] + nums[j] + nums[k] == 0: 14 | temp = [nums[i], nums[j], nums[k]] 15 | temp.sort() 16 | my_set.add(tuple(temp)) 17 | 18 | ans = [list(item) for item in my_set] 19 | return ans 20 | -------------------------------------------------------------------------------- /5. Lists/Hard/Leetcode 152 - Maximum Product Subarray/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def maxProduct(self, nums: List[int]) -> int: 6 | n = len(nums) 7 | maxi = float("-inf") 8 | for i in range(0, n): 9 | product = 1 10 | for j in range(i, n): 11 | product *= nums[j] 12 | maxi = max(maxi, product) 13 | return maxi 14 | -------------------------------------------------------------------------------- /5. Lists/Hard/Leetcode 18 - 4Sum/better.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def fourSum(self, nums: List[int], target: int) -> List[List[int]]: 6 | n = len(nums) 7 | if n < 4: 8 | return [] 9 | my_set = set() 10 | for i in range(0, n): 11 | for j in range(i + 1, n): 12 | hash_set = set() 13 | for k in range(j + 1, n): 14 | fourth = target - (nums[i] + nums[j] + nums[k]) 15 | if fourth in hash_set: 16 | temp = [nums[i], nums[j], nums[k], fourth] 17 | temp.sort() 18 | my_set.add(tuple(temp)) 19 | hash_set.add(nums[k]) 20 | result = [list(ans) for ans in my_set] 21 | return result 22 | -------------------------------------------------------------------------------- /5. Lists/Hard/Leetcode 18 - 4Sum/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def fourSum(self, nums: List[int], target: int) -> List[List[int]]: 6 | n = len(nums) 7 | if n < 4: 8 | return [] 9 | my_set = set() 10 | for i in range(0, n): 11 | for j in range(i + 1, n): 12 | for k in range(j + 1, n): 13 | for l in range(k + 1, n): 14 | total = nums[i] + nums[j] + nums[k] + nums[l] 15 | if total == target: 16 | temp = [nums[i], nums[j], nums[k], nums[l]] 17 | temp.sort() 18 | my_set.add(tuple(temp)) 19 | result = [] 20 | for ans in my_set: 21 | result.append(list(ans)) 22 | return result 23 | -------------------------------------------------------------------------------- /5. Lists/Hard/Leetcode 493 - Reverse Pairs/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def reversePairs(self, nums: List[int]) -> int: 6 | count = 0 7 | n = len(nums) 8 | for i in range(0, n): 9 | for j in range(i + 1, n): 10 | if nums[i] > 2 * nums[j]: 11 | count += 1 12 | return count 13 | -------------------------------------------------------------------------------- /5. Lists/Hard/Leetcode 56 - Merge Intervals/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def merge(self, intervals: List[List[int]]) -> List[List[int]]: 6 | n = len(intervals) 7 | intervals.sort() 8 | ans = [] 9 | for i in range(n): 10 | start, end = intervals[i][0], intervals[i][1] 11 | 12 | # Skip intervals if in between 13 | if len(ans) != 0 and end <= ans[-1][1]: 14 | continue 15 | 16 | # Go for futher intervals 17 | for j in range(i + 1, n): 18 | if intervals[j][0] <= end: 19 | end = max(end, intervals[j][1]) 20 | else: 21 | break 22 | ans.append([start, end]) 23 | return ans 24 | -------------------------------------------------------------------------------- /5. Lists/Hard/Leetcode 56 - Merge Intervals/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def merge(self, intervals: List[List[int]]) -> List[List[int]]: 6 | n = len(intervals) 7 | intervals.sort() 8 | ans = [] 9 | for i in range(n): 10 | if len(ans) == 0 or intervals[i][0] > ans[-1][1]: 11 | ans.append(intervals[i]) 12 | else: 13 | ans[-1][1] = max(ans[-1][1], intervals[i][1]) 14 | return ans 15 | -------------------------------------------------------------------------------- /5. Lists/Hard/Majority Element 2/better.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def majorityElement(self, nums: List[int]) -> List[int]: 6 | n = len(nums) 7 | freq = {} 8 | for num in nums: 9 | freq[num] = freq.get(num, 0) + 1 10 | result = [] 11 | for k, v in freq.items(): 12 | if v > n // 3: 13 | result.append(k) 14 | if len(result) == 2: 15 | break 16 | return result 17 | -------------------------------------------------------------------------------- /5. Lists/Hard/Majority Element 2/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def majorityElement(self, nums: List[int]) -> List[int]: 6 | n = len(nums) 7 | result = [] 8 | for i in range(0, n): 9 | if len(result) == 0 or result[0] != nums[i]: 10 | count = 0 11 | for j in range(0, n): 12 | if nums[j] == nums[i]: 13 | count += 1 14 | if count > n // 3: 15 | result.append(nums[i]) 16 | if len(result) == 2: 17 | break 18 | return result 19 | -------------------------------------------------------------------------------- /5. Lists/Hard/Max Subarray with Sum 0/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity - O(n^2) 3 | Space complexity - O(1) 4 | """ 5 | 6 | 7 | class Solution: 8 | def maxLen(self, n, arr): 9 | max_length = 0 10 | n = len(arr) 11 | for i in range(n): 12 | sum = 0 13 | for j in range(i, n): 14 | sum += arr[j] 15 | if sum == 0: 16 | max_length = max(max_length, j - i + 1) 17 | return max_length 18 | -------------------------------------------------------------------------------- /5. Lists/Hard/Max Subarray with Sum 0/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maxLen(self, n, arr): 3 | n = len(arr) 4 | prefix_sum = {} 5 | maxi = 0 6 | sum = 0 7 | for i in range(n): 8 | sum += arr[i] 9 | if sum == 0: 10 | maxi = i + 1 11 | else: 12 | if sum in prefix_sum: 13 | maxi = max(maxi, i - prefix_sum[sum]) 14 | else: 15 | prefix_sum[sum] = i 16 | return maxi 17 | -------------------------------------------------------------------------------- /5. Lists/Hard/Merge two sorted arrays without extra space/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def merge(self, arr1, arr2, n, m): 3 | left = n - 1 4 | right = 0 5 | 6 | while left >= 0 and right < m: 7 | if arr1[left] > arr2[right]: 8 | arr1[left], arr2[right] = arr2[right], arr1[left] 9 | left -= 1 10 | right += 1 11 | else: 12 | break 13 | 14 | arr1.sort() 15 | arr2.sort() 16 | -------------------------------------------------------------------------------- /5. Lists/Hard/Pascals Triangle/variation-3-optimal.py: -------------------------------------------------------------------------------- 1 | from typing import * 2 | 3 | 4 | def generateRow(row): 5 | ans = 1 6 | ansRow = [1] 7 | 8 | for col in range(1, row): 9 | ans *= row - col 10 | ans //= col 11 | ansRow.append(ans) 12 | return ansRow 13 | 14 | 15 | def pascalTriangle(n: int) -> List[List[int]]: 16 | ans = [] 17 | 18 | for row in range(1, n + 1): 19 | ans.append(generateRow(row)) 20 | return ans 21 | 22 | 23 | if __name__ == "__main__": 24 | n = 5 25 | ans = pascalTriangle(n) 26 | print(ans) 27 | -------------------------------------------------------------------------------- /5. Lists/Medium/1. Longest Subarray With Sum K/better.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(n) 3 | n is number of elements in nums 4 | 5 | Space complexity -> O(n) 6 | """ 7 | 8 | 9 | def longestSubarrayWithSumK(a: [int], k: int) -> int: 10 | sum_map = dict() 11 | Sum = 0 12 | max_length = 0 13 | for i in range(0, len(a)): 14 | Sum += a[i] 15 | if Sum == k: 16 | max_length = i + 1 17 | 18 | rem = Sum - k 19 | if rem in sum_map: 20 | l = i - sum_map[rem] 21 | max_length = max(max_length, l) 22 | 23 | if Sum not in sum_map: 24 | sum_map[Sum] = i 25 | 26 | return max_length 27 | -------------------------------------------------------------------------------- /5. Lists/Medium/1. Longest Subarray With Sum K/brute-force-2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(n^2) 3 | n is number of elements in nums 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | # Part 2 10 | def longestSubarrayWithSumK(a: [int], k: int) -> int: 11 | n = len(a) 12 | 13 | length = 0 14 | for i in range(n): 15 | s = 0 16 | for j in range(i, n): 17 | s += a[j] 18 | if s == k: 19 | length = max(length, j - i + 1) 20 | return length 21 | -------------------------------------------------------------------------------- /5. Lists/Medium/1. Longest Subarray With Sum K/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(n^3) 3 | n is number of elements in nums 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | def longestSubarrayWithSumK(a: [int], k: int) -> int: 10 | n = len(a) 11 | 12 | length = 0 13 | for i in range(n): 14 | for j in range(i, n): 15 | s = 0 16 | for K in range(i, j + 1): 17 | s += a[K] 18 | if s == k: 19 | length = max(length, j - i + 1) 20 | return length 21 | -------------------------------------------------------------------------------- /5. Lists/Medium/1. Longest Subarray With Sum K/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(2N) 3 | n is number of elements in nums 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | def longestSubarrayWithSumK(a: [int], k: int) -> int: 10 | n = len(a) 11 | left = 0 12 | right = 0 13 | max_length = 0 14 | Sum = a[0] 15 | while right < n: 16 | while left <= right and Sum > k: 17 | Sum -= a[left] 18 | left += 1 19 | if Sum == k: 20 | max_length = max(max_length, right - left + 1) 21 | 22 | right += 1 23 | if right < n: 24 | Sum += a[right] 25 | return max_length 26 | -------------------------------------------------------------------------------- /5. Lists/Medium/10. Superior Elements/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N) 5 | N is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | def superiorElements(arr: List[int]) -> List[int]: 12 | result = [] 13 | maxi = float("-inf") 14 | n = len(arr) 15 | for i in range(n - 1, -1, -1): 16 | e = max(maxi, arr[i]) 17 | if e > maxi: 18 | result.append(e) 19 | maxi = e 20 | return result 21 | -------------------------------------------------------------------------------- /5. Lists/Medium/11. Longest Successive Elements/better.py: -------------------------------------------------------------------------------- 1 | from typing import * 2 | 3 | """ 4 | Time complexity -> O(NlogN + N) 5 | N is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | def longestSuccessiveElements(arr: List[int]) -> int: 12 | arr.sort() 13 | last_smaller = float("-inf") 14 | count = 0 15 | longest = 0 16 | for num in arr: 17 | if num - 1 == last_smaller: 18 | count += 1 19 | last_smaller = num 20 | elif num != last_smaller: 21 | count = 1 22 | last_smaller = num 23 | longest = max(longest, count) 24 | return longest 25 | -------------------------------------------------------------------------------- /5. Lists/Medium/11. Longest Successive Elements/brute.py: -------------------------------------------------------------------------------- 1 | from typing import * 2 | 3 | """ 4 | Time complexity -> O(N^2) 5 | N is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | def longestSuccessiveElements(arr: List[int]) -> int: 12 | max_count = 0 13 | n = len(arr) 14 | for i in range(0, n): 15 | num = arr[i] 16 | count = 1 17 | while num + 1 in arr: 18 | count += 1 19 | num += 1 20 | max_count = max(max_count, count) 21 | return max_count 22 | -------------------------------------------------------------------------------- /5. Lists/Medium/11. Longest Successive Elements/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import * 2 | 3 | """ 4 | Time complexity -> O(N+2N) -> O(3N) 5 | N is number of elements in nums 6 | 7 | Space complexity -> O(N) 8 | """ 9 | 10 | 11 | from typing import * 12 | 13 | 14 | def longestSuccessiveElements(arr: List[int]) -> int: 15 | my_set = set() 16 | # Same as my_set=set(arr) 17 | for num in arr: 18 | my_set.add(num) 19 | longest = 0 20 | for num in my_set: 21 | if num - 1 not in my_set: 22 | x = num 23 | count = 1 24 | while x + 1 in my_set: 25 | count += 1 26 | x += 1 27 | longest = max(longest, count) 28 | return longest 29 | -------------------------------------------------------------------------------- /5. Lists/Medium/12. Leetcode 73 - Set Matrix Zeros/better.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def setZeroes(self, matrix: List[List[int]]) -> None: 6 | """ 7 | Do not return anything, modify matrix in-place instead. 8 | """ 9 | r = len(matrix) 10 | c = len(matrix[0]) 11 | rowTrack = [0 for _ in range(r)] 12 | colTrack = [0 for _ in range(c)] 13 | for i in range(r): 14 | for j in range(c): 15 | if matrix[i][j] == 0: 16 | rowTrack[i] = -1 17 | colTrack[j] = -1 18 | 19 | for i in range(r): 20 | for j in range(c): 21 | if rowTrack[i] == -1 or colTrack[j] == -1: 22 | matrix[i][j] = 0 23 | -------------------------------------------------------------------------------- /5. Lists/Medium/14 . Leetcode 48 - Rotate Image/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def rotate(self, matrix: List[List[int]]) -> None: 6 | """ 7 | Do not return anything, modify matrix in-place instead. 8 | """ 9 | r = len(matrix) 10 | c = len(matrix[0]) 11 | result = [[0 for _ in range(c)] for _ in range(r)] 12 | for i in range(r): 13 | for j in range(c): 14 | result[j][r - 1 - i] = matrix[i][j] 15 | matrix[:] = result 16 | -------------------------------------------------------------------------------- /5. Lists/Medium/14 . Leetcode 48 - Rotate Image/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def rotate(self, matrix: List[List[int]]) -> None: 6 | """ 7 | Do not return anything, modify matrix in-place instead. 8 | """ 9 | n = len(matrix) 10 | for i in range(n - 1): 11 | for j in range(i + 1, n): 12 | matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] 13 | for i in range(n): 14 | matrix[i].reverse() 15 | -------------------------------------------------------------------------------- /5. Lists/Medium/2. Longest Subarray With Sum K/better.py: -------------------------------------------------------------------------------- 1 | from sys import * 2 | from collections import * 3 | from math import * 4 | 5 | """ 6 | Time complexity -> O(n) 7 | n is number of elements in nums 8 | 9 | Space complexity -> O(n) 10 | """ 11 | 12 | 13 | def getLongestSubarray(nums: [int], k: int) -> int: 14 | sum_map = dict() 15 | Sum = 0 16 | max_length = 0 17 | for i in range(0, len(nums)): 18 | Sum += nums[i] 19 | if Sum == k: 20 | max_length = i + 1 21 | 22 | rem = Sum - k 23 | if rem in sum_map: 24 | l = i - sum_map[rem] 25 | max_length = max(max_length, l) 26 | 27 | if Sum not in sum_map: 28 | sum_map[Sum] = i 29 | 30 | return max_length 31 | -------------------------------------------------------------------------------- /5. Lists/Medium/2. Longest Subarray With Sum K/brute-force-2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(n^2) 3 | n is number of elements in nums 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | # Part 2 10 | def getLongestSubarray(nums: [int], k: int) -> int: 11 | n = len(nums) 12 | 13 | length = 0 14 | for i in range(n): 15 | s = 0 16 | for j in range(i, n): 17 | s += nums[j] 18 | if s == k: 19 | length = max(length, j - i + 1) 20 | return length 21 | -------------------------------------------------------------------------------- /5. Lists/Medium/2. Longest Subarray With Sum K/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(n^3) 3 | n is number of elements in nums 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | def longestSubarrayWithSumK(nums: [int], k: int) -> int: 10 | n = len(nums) 11 | 12 | length = 0 13 | for i in range(n): 14 | for j in range(i, n): 15 | s = 0 16 | for K in range(i, j + 1): 17 | s += nums[K] 18 | if s == k: 19 | length = max(length, j - i + 1) 20 | return length 21 | -------------------------------------------------------------------------------- /5. Lists/Medium/3. Leetcode 1 - Two Sum/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n^2) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def twoSum(self, nums: List[int], target: int) -> List[int]: 13 | n = len(nums) 14 | for i in range(0, n): 15 | for j in range(i + 1, n): 16 | if nums[i] + nums[j] == target: 17 | return [i, j] 18 | -------------------------------------------------------------------------------- /5. Lists/Medium/3. Leetcode 1 - Two Sum/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(n) 8 | """ 9 | 10 | 11 | class Solution: 12 | def twoSum(self, nums: List[int], target: int) -> List[int]: 13 | n = len(nums) 14 | hash_map = dict() 15 | for i in range(n): 16 | remaining = target - nums[i] 17 | if remaining in hash_map: 18 | return [hash_map[remaining], i] 19 | hash_map[nums[i]] = i 20 | -------------------------------------------------------------------------------- /5. Lists/Medium/4. Leetcode 75 - Sort Colors/better.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(2N) -> O(N) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def sortColors(self, nums: List[int]) -> None: 13 | """ 14 | Do not return anything, modify nums in-place instead. 15 | """ 16 | n = len(nums) 17 | cnt1 = 0 18 | cnt2 = 0 19 | cnt3 = 0 20 | for i in range(0, n): 21 | if nums[i] == 0: 22 | cnt1 += 1 23 | elif nums[i] == 1: 24 | cnt2 += 1 25 | else: 26 | cnt3 += 1 27 | for i in range(0, cnt1): 28 | nums[i] = 0 29 | for i in range(cnt1, cnt1 + cnt2): 30 | nums[i] = 1 31 | for i in range(cnt1 + cnt2, cnt1 + cnt2 + cnt3): 32 | nums[i] = 2 33 | -------------------------------------------------------------------------------- /5. Lists/Medium/4. Leetcode 75 - Sort Colors/brute.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n logn) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def sortColors(self, nums: List[int]) -> None: 13 | """ 14 | Do not return anything, modify nums in-place instead. 15 | """ 16 | nums.sort() 17 | -------------------------------------------------------------------------------- /5. Lists/Medium/4. Leetcode 75 - Sort Colors/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | # Also known as Dutch National Flag Algorithm 11 | 12 | 13 | class Solution: 14 | def sortColors(self, nums: List[int]) -> None: 15 | """ 16 | Do not return anything, modify nums in-place instead. 17 | """ 18 | low = 0 19 | mid = 0 20 | high = len(nums) - 1 21 | 22 | while mid <= high: 23 | if nums[mid] == 0: 24 | nums[low], nums[mid] = nums[mid], nums[low] 25 | low += 1 26 | mid += 1 27 | elif nums[mid] == 1: 28 | mid += 1 29 | else: 30 | nums[mid], nums[high] = nums[high], nums[mid] 31 | high -= 1 32 | -------------------------------------------------------------------------------- /5. Lists/Medium/5. Leetcode 169 - Majority Element/better.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(2n) -> O(n) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(n) 8 | """ 9 | 10 | 11 | class Solution: 12 | def majorityElement(self, nums: List[int]) -> int: 13 | hash_map = dict() 14 | n = len(nums) 15 | for num in nums: 16 | hash_map[num] = hash_map.get(num, 0) + 1 17 | for k, v in hash_map.items(): 18 | if v > n // 2: 19 | return k 20 | -------------------------------------------------------------------------------- /5. Lists/Medium/5. Leetcode 169 - Majority Element/brute.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n^2) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def majorityElement(self, nums: List[int]) -> int: 13 | n = len(nums) 14 | for i in range(n): 15 | cnt = 0 16 | for j in range(n): 17 | if nums[j] == nums[i]: 18 | cnt += 1 19 | 20 | if cnt > (n // 2): 21 | return nums[i] 22 | -------------------------------------------------------------------------------- /5. Lists/Medium/5. Leetcode 169 - Majority Element/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | # Also known as Moore’s Voting Algorithm 11 | 12 | 13 | class Solution: 14 | def majorityElement(self, nums: List[int]) -> int: 15 | candidate = nums[0] 16 | count = 0 17 | for i in range(0, len(nums)): 18 | if count == 0: 19 | candidate = nums[i] 20 | count = 1 21 | elif nums[i] == candidate: 22 | count += 1 23 | else: 24 | count -= 1 25 | return candidate 26 | -------------------------------------------------------------------------------- /5. Lists/Medium/6. Leetcode 53 - Maximum Subarray/better.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n^2) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def maxSubArray(self, nums: List[int]) -> int: 13 | maxi = float("-inf") 14 | n = len(nums) 15 | for i in range(n): 16 | sum = 0 17 | for j in range(i, n): 18 | 19 | # add the current element arr[j] 20 | # to the sum i.e. sum of arr[i...j-1] 21 | sum += nums[j] 22 | 23 | maxi = max(maxi, sum) # getting the maximum 24 | return maxi 25 | -------------------------------------------------------------------------------- /5. Lists/Medium/6. Leetcode 53 - Maximum Subarray/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n^3) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def maxSubArray(self, nums: List[int]) -> int: 13 | maxi = float("-inf") 14 | n = len(nums) 15 | for i in range(n): 16 | for j in range(i, n): 17 | summ = 0 18 | 19 | # add all the elements of subarray 20 | for k in range(i, j + 1): 21 | summ += nums[k] 22 | 23 | maxi = max(maxi, summ) 24 | return maxi 25 | -------------------------------------------------------------------------------- /5. Lists/Medium/6. Leetcode 53 - Maximum Subarray/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | # Also known as Kadane’s Algorithm 11 | 12 | 13 | class Solution: 14 | def maxSubArray(self, nums: List[int]) -> int: 15 | maxi = float("-inf") 16 | Sum = 0 17 | n = len(nums) 18 | for i in range(n): 19 | Sum += nums[i] 20 | 21 | if Sum > maxi: 22 | maxi = Sum 23 | 24 | # If sum < 0: discard the sum calculated 25 | if Sum < 0: 26 | Sum = 0 27 | 28 | # To consider the sum of the empty subarray 29 | # uncomment the following check: 30 | 31 | # if maxi < 0: maxi = 0 32 | 33 | return maxi 34 | -------------------------------------------------------------------------------- /5. Lists/Medium/7. Leetcode 121 - Best Time to Buy and Sell Stock/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n^2) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def maxProfit(self, prices: List[int]) -> int: 13 | maxPro = 0 14 | n = len(prices) 15 | 16 | for i in range(n): 17 | for j in range(i + 1, n): 18 | if prices[j] > prices[i]: 19 | maxPro = max(prices[j] - prices[i], maxPro) 20 | 21 | return maxPro 22 | -------------------------------------------------------------------------------- /5. Lists/Medium/7. Leetcode 121 - Best Time to Buy and Sell Stock/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def maxProfit(self, prices: List[int]) -> int: 13 | max_profit = 0 14 | min_price = float("inf") 15 | for i in range(len(prices)): 16 | min_price = min(min_price, prices[i]) 17 | max_profit = max(max_profit, prices[i] - min_price) 18 | return max_profit 19 | -------------------------------------------------------------------------------- /5. Lists/Medium/8. Leetcode 2149 - Rearrange Array Elements by Sign/brute.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N+N/2) 5 | N is number of elements in nums 6 | 7 | Space complexity -> O(N/2 + N/2) 8 | """ 9 | 10 | 11 | class Solution: 12 | def rearrangeArray(self, nums: List[int]) -> List[int]: 13 | pos = [] 14 | neg = [] 15 | n = len(nums) 16 | for i in range(len(nums)): 17 | if nums[i] > 0: 18 | pos.append(nums[i]) 19 | else: 20 | neg.append(nums[i]) 21 | 22 | for i in range(len(pos)): 23 | nums[2 * i] = pos[i] 24 | nums[2 * i + 1] = neg[i] 25 | 26 | return nums 27 | -------------------------------------------------------------------------------- /5. Lists/Medium/8. Leetcode 2149 - Rearrange Array Elements by Sign/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N) 5 | N is number of elements in nums 6 | 7 | Space complexity -> O(N) 8 | """ 9 | 10 | 11 | class Solution: 12 | def rearrangeArray(self, nums: List[int]) -> List[int]: 13 | n = len(nums) 14 | ans = [0] * n 15 | posIndex, negIndex = 0, 1 16 | for i in range(n): 17 | if nums[i] < 0: 18 | ans[negIndex] = nums[i] 19 | negIndex += 2 20 | else: 21 | ans[posIndex] = nums[i] 22 | posIndex += 2 23 | return ans 24 | -------------------------------------------------------------------------------- /5. Lists/Medium/9. Leetcode 31 - Next Permutation/optimal.py.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(3N) 5 | N is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def nextPermutation(self, nums: List[int]) -> None: 13 | n = len(nums) 14 | 15 | ind = -1 16 | for i in range(n - 2, -1, -1): 17 | if nums[i] < nums[i + 1]: 18 | ind = i 19 | break 20 | 21 | if ind == -1: 22 | nums.reverse() 23 | return nums 24 | 25 | # Step 2: Find the next greater element 26 | # and swap it with nums[ind]: 27 | for i in range(n - 1, ind, -1): 28 | if nums[i] > nums[ind]: 29 | nums[i], nums[ind] = nums[ind], nums[i] 30 | break 31 | 32 | # Step 3: reverse the right half: 33 | nums[ind + 1 :] = reversed(nums[ind + 1 :]) 34 | -------------------------------------------------------------------------------- /5. Lists/Medium/Permutation/approach-1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N! * N) 3 | N is number of elements in nums 4 | 5 | Space complexity -> O(N) 6 | """ 7 | 8 | 9 | def permutation(nums, freq, ds, ans): 10 | if len(ds) == len(nums): 11 | ans.append(ds.copy()) 12 | return 13 | for i in range(len(nums)): 14 | if freq[i] == 0: 15 | ds.append(nums[i]) 16 | freq[i] = 1 17 | permutation(nums, freq, ds, ans) 18 | freq[i] = 0 19 | ds.pop() 20 | 21 | 22 | ans = [] 23 | permutation([5, 7, 9, 1], [0, 0, 0, 0], [], ans) 24 | print(ans) 25 | -------------------------------------------------------------------------------- /5. Lists/Medium/Permutation/approach-2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N! * N) 3 | N is number of elements in nums 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | def permutation(nums, index, ans): 10 | if index == len(nums): 11 | ans.append(nums.copy()) 12 | return 13 | for i in range(index, len(nums)): 14 | nums[index], nums[i] = nums[i], nums[index] 15 | permutation(nums, index + 1, ans) 16 | nums[index], nums[i] = nums[i], nums[index] 17 | 18 | 19 | ans = [] 20 | permutation([1, 2, 3, 4], 1, ans) 21 | print(ans) 22 | -------------------------------------------------------------------------------- /6. Binary Search/Easy/1. Leetcode 35 - Search Insert Position/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(logn) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def searchInsert(self, nums: List[int], target: int) -> int: 13 | n = len(nums) 14 | low = 0 15 | high = n - 1 16 | ans = n 17 | while low <= high: 18 | mid = (low + high) // 2 19 | if nums[mid] >= target: 20 | ans = mid 21 | high = mid - 1 22 | else: 23 | low = mid + 1 24 | return ans 25 | -------------------------------------------------------------------------------- /6. Binary Search/Easy/2. Ceil The Floor/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(logn) 3 | n is number of elements in nums 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | def getFloorAndCeil(a, n, x): 10 | floor = -1 11 | ceil = -1 12 | low = 0 13 | high = n - 1 14 | while low <= high: 15 | mid = (low + high) // 2 16 | if a[mid] == x: 17 | return x, x 18 | elif a[mid] > x: 19 | ceil = a[mid] 20 | high = mid - 1 21 | else: 22 | floor = a[mid] 23 | low = mid + 1 24 | return floor, ceil 25 | -------------------------------------------------------------------------------- /6. Binary Search/Easy/3. Leetcode 34 - Find First and Last Position of Element in Sorted Array/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(n) 5 | n is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def searchRange(self, nums: List[int], target: int) -> List[int]: 13 | first = -1 14 | last = -1 15 | for i in range(len(nums)): 16 | if nums[i] == target: 17 | if first == -1: 18 | first = i 19 | last = i 20 | return [first, last] 21 | -------------------------------------------------------------------------------- /6. Binary Search/Easy/4. Leetcode 704 - Binary Search/recursive.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(logN) 5 | N is the number of elements in nums 6 | 7 | Space complexity -> O(logN) (Stack Space) 8 | """ 9 | 10 | 11 | class Solution: 12 | def search(self, nums: List[int], target: int) -> int: 13 | def recursive(nums, low, high, target): 14 | if low > high: 15 | return -1 16 | mid = (low + high) // 2 17 | if nums[mid] == target: 18 | return mid 19 | elif nums[mid] < target: 20 | return recursive(nums, mid + 1, high, target) 21 | else: 22 | return recursive(nums, low, mid - 1, target) 23 | 24 | return recursive(nums, 0, len(nums) - 1, target) 25 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/1. Number of occurrence/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) 3 | N is number of elements in nums 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | def count(arr: [int], n: int, target: int) -> int: 10 | cnt = 0 11 | for i in range(n): 12 | if arr[i] == target: 13 | cnt += 1 14 | return cnt 15 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/1. Number of occurrence/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(2 * logN) -> O(logN) 3 | N is number of elements in nums 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | def count(arr: [int], n: int, target: int) -> int: 10 | def loweBound(arr, n, target): 11 | ind = n 12 | low = 0 13 | high = n - 1 14 | while low <= high: 15 | mid = (low + high) // 2 16 | if arr[mid] >= target: 17 | ind = mid 18 | high = mid - 1 19 | else: 20 | low = mid + 1 21 | return ind 22 | 23 | def upperBound(arr, n, target): 24 | ind = n 25 | low = 0 26 | high = n - 1 27 | while low <= high: 28 | mid = (low + high) // 2 29 | if arr[mid] > target: 30 | ind = mid 31 | high = mid - 1 32 | else: 33 | low = mid + 1 34 | return ind 35 | 36 | return upperBound(arr, n, target) - loweBound(arr, n, target) 37 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/2. Leetcode 33 – Search in Rotated Sorted Array/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N) 5 | N is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def search(self, nums: List[int], target: int) -> int: 13 | for i in range(len(nums)): 14 | if nums[i] == target: 15 | return i 16 | return -1 17 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/2. Leetcode 33 – Search in Rotated Sorted Array/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(logN) 5 | N is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def search(self, nums: List[int], target: int) -> int: 13 | n = len(nums) 14 | low = 0 15 | high = n - 1 16 | while low <= high: 17 | mid = (low + high) // 2 18 | if nums[mid] == target: 19 | return mid 20 | if nums[low] <= nums[mid]: 21 | if nums[low] <= target <= nums[mid]: 22 | high = mid - 1 23 | else: 24 | low = mid + 1 25 | else: 26 | if nums[mid] <= target <= nums[high]: 27 | low = mid + 1 28 | else: 29 | high = mid - 1 30 | 31 | return -1 32 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/3. Leetcode 81 - Search in Rotated Sorted Array II/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N) 5 | N is number of elements in nums 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def search(self, nums: List[int], target: int) -> bool: 13 | for i in range(len(nums)): 14 | if nums[i] == target: 15 | return True 16 | return False 17 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/4. Leetcode 153 - Find Minimum in Rotated Sorted Array/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N) 5 | Here, N = size of the given array. 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def findMin(self, nums: List[int]) -> int: 13 | minimum = float("inf") 14 | for i in range(len(nums)): 15 | minimum = min(minimum, nums[i]) 16 | return minimum 17 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/4. Leetcode 153 - Find Minimum in Rotated Sorted Array/optimal1.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(logN) 5 | Here, N = size of the given array. 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def findMin(self, nums: List[int]) -> int: 13 | n = len(nums) 14 | low = 0 15 | high = n - 1 16 | minimum = float("inf") 17 | while low <= high: 18 | mid = (low + high) // 2 19 | if nums[low] <= nums[mid]: 20 | minimum = min(minimum, nums[low]) 21 | low = mid + 1 22 | else: 23 | minimum = min(minimum, nums[mid]) 24 | high = mid - 1 25 | return minimum 26 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/4. Leetcode 153 - Find Minimum in Rotated Sorted Array/optimal2.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(logN) 5 | Here, N = size of the given array. 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def findMin(self, nums: List[int]) -> int: 13 | n = len(nums) 14 | low = 0 15 | high = n - 1 16 | minimum = float("inf") 17 | while low <= high: 18 | mid = (low + high) // 2 19 | if nums[low] <= nums[high]: 20 | minimum = min(minimum, nums[low]) 21 | break 22 | if nums[low] <= nums[mid]: 23 | minimum = min(minimum, nums[low]) 24 | low = mid + 1 25 | else: 26 | minimum = min(minimum, nums[mid]) 27 | high = mid - 1 28 | return minimum 29 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/5. Rotation/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N) 5 | Here, N = size of the given array. 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def findKRotation(self, arr): 13 | n = len(arr) 14 | for i in range(0, n - 1): 15 | if arr[i] > arr[i + 1]: 16 | return i + 1 17 | return 0 18 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/5. Rotation/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(logN) 5 | Here, N = size of the given array. 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | def findKRotation(nums: List[int]) -> int: 12 | n = len(nums) 13 | low = 0 14 | high = n - 1 15 | minimum = float("inf") 16 | index = -1 17 | while low <= high: 18 | mid = (low + high) // 2 19 | if nums[low] <= nums[high]: 20 | if nums[low] < minimum: 21 | minimum = nums[low] 22 | index = low 23 | break 24 | if nums[low] <= nums[mid]: 25 | if nums[low] < minimum: 26 | minimum = nums[low] 27 | index = low 28 | low = mid + 1 29 | else: 30 | if nums[mid] < minimum: 31 | minimum = nums[mid] 32 | index = mid 33 | high = mid - 1 34 | return index 35 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/6. Leetcode 540 - Single Element in a Sorted Array/brute.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N) 5 | Here, N = size of the given array. 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def singleNonDuplicate(self, arr: List[int]) -> int: 13 | n = len(arr) 14 | if n == 1: 15 | return arr[0] 16 | for i in range(n): 17 | if i == 0: 18 | if arr[i] != arr[i + 1]: 19 | return arr[i] 20 | elif i == n - 1: 21 | if arr[i] != arr[i - 1]: 22 | return arr[i] 23 | else: 24 | if arr[i] != arr[i - 1] and arr[i] != arr[i + 1]: 25 | return arr[i] 26 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/6. Leetcode 540 - Single Element in a Sorted Array/optimal1.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(logN) 5 | Here, N = size of the given array. 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def singleNonDuplicate(self, arr: List[int]) -> int: 13 | low = 0 14 | high = len(arr) - 1 15 | while low <= high: 16 | mid = (low + high) // 2 17 | if low == high: 18 | return arr[low] 19 | if arr[mid] != arr[mid - 1] and arr[mid] != arr[mid + 1]: 20 | return arr[mid] 21 | if arr[mid] == arr[mid - 1]: 22 | if mid % 2 == 1: 23 | low = mid + 1 24 | else: 25 | high = mid - 1 26 | elif arr[mid] == arr[mid + 1]: 27 | if mid % 2 == 1: 28 | high = mid - 1 29 | else: 30 | low = mid + 1 31 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/6. Leetcode 540 - Single Element in a Sorted Array/optimal2.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(logN) 5 | Here, N = size of the given array. 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def singleNonDuplicate(self, arr: List[int]) -> int: 13 | n = len(arr) 14 | if n == 1: 15 | return arr[0] 16 | if arr[0] != arr[1]: 17 | return arr[0] 18 | if arr[n - 1] != arr[n - 2]: 19 | return arr[n - 1] 20 | low = 1 21 | high = n - 2 22 | while low <= high: 23 | mid = (low + high) // 2 24 | if arr[mid] != arr[mid - 1] and arr[mid] != arr[mid + 1]: 25 | return arr[mid] 26 | elif (arr[mid] == arr[mid - 1] and mid % 2 == 1) or ( 27 | arr[mid] == arr[mid + 1] and mid % 2 == 0 28 | ): 29 | low = mid + 1 30 | else: 31 | high = mid - 1 32 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/7. Leetcode 162 - Find Peak Element/brute.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(N) 5 | Here, N = size of the given array. 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def findPeakElement(self, nums: List[int]) -> int: 13 | n = len(nums) 14 | if n == 1: 15 | return 0 16 | elif nums[0] > nums[1]: 17 | return 0 18 | elif nums[n - 1] > nums[n - 2]: 19 | return n - 1 20 | for i in range(1, n - 1): 21 | if nums[i] > nums[i - 1] and nums[i] > nums[i + 1]: 22 | return i 23 | -------------------------------------------------------------------------------- /6. Binary Search/Medium/7. Leetcode 162 - Find Peak Element/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | """ 4 | Time complexity -> O(logN) 5 | Here, N = size of the given array. 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def findPeakElement(self, nums: List[int]) -> int: 13 | if len(nums) == 1: 14 | return 0 15 | if nums[0] > nums[1]: 16 | return 0 17 | elif nums[-1] > nums[-2]: 18 | return len(nums) - 1 19 | low = 1 20 | high = len(nums) - 2 21 | while low <= high: 22 | mid = (low + high) // 2 23 | if nums[mid] > nums[mid - 1] and nums[mid] > nums[mid + 1]: 24 | return mid 25 | if nums[mid] > nums[mid + 1]: 26 | high = mid - 1 27 | else: 28 | low = mid + 1 29 | -------------------------------------------------------------------------------- /7. Binary Search on Answers/1. Square Root of a number/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) 3 | N is the number 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | def floorSqrt(n): 10 | ans = 0 11 | for i in range(0, n + 1): 12 | if i * i <= n: 13 | ans = i 14 | else: 15 | break 16 | return ans 17 | 18 | 19 | n = int(input()) 20 | print(floorSqrt(n)) 21 | -------------------------------------------------------------------------------- /7. Binary Search on Answers/1. Square Root of a number/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(logN) 3 | N is the number 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | def floorSqrt(n): 10 | low = 0 11 | high = n 12 | ans = 0 13 | while low <= high: 14 | mid = (low + high) // 2 15 | if mid * mid <= n: 16 | ans = mid 17 | low = mid + 1 18 | else: 19 | high = mid - 1 20 | return high 21 | 22 | 23 | n = int(input()) 24 | print(floorSqrt(n)) 25 | -------------------------------------------------------------------------------- /7. Binary Search on Answers/10. Kth Missing Positive Number/brute-force.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findKthPositive(self, arr: List[int], k: int) -> int: 3 | n = len(arr) 4 | for i in range(0, n): 5 | if arr[i] < k: 6 | k += 1 7 | else: 8 | break 9 | return k 10 | -------------------------------------------------------------------------------- /7. Binary Search on Answers/2. Find Nth Root Of M/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) 3 | N is the number 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | def NthRoot(n: int, m: int) -> int: 10 | for i in range(1, m + 1): 11 | if i**n == m: 12 | return i 13 | return -1 14 | -------------------------------------------------------------------------------- /7. Binary Search on Answers/2. Find Nth Root Of M/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(logN) 3 | N is the number 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | def NthRoot(n: int, m: int) -> int: 10 | low = 1 11 | high = m 12 | while low <= high: 13 | mid = (low + high) // 2 14 | if mid**n == m: 15 | return mid 16 | elif mid**n < m: 17 | low = mid + 1 18 | else: 19 | high = mid - 1 20 | return -1 21 | -------------------------------------------------------------------------------- /7. Binary Search on Answers/3. Leetcode 875 - Koko Eating Bananas/brute-force.py: -------------------------------------------------------------------------------- 1 | import math 2 | from typing import List 3 | 4 | """ 5 | Time complexity -> O(N * max(piles)) 6 | N is the number 7 | 8 | Space complexity -> O(1) 9 | """ 10 | 11 | 12 | class Solution: 13 | def minEatingSpeed(self, piles: List[int], h: int) -> int: 14 | def totalHours(piles, hourly_rate): 15 | total = 0 16 | for i in range(len(piles)): 17 | total = total + math.ceil(piles[i] / hourly_rate) 18 | return total 19 | 20 | maximum_banana = max(piles) 21 | for i in range(1, maximum_banana + 1): 22 | total_hour = totalHours(piles, i) 23 | if total_hour <= h: 24 | return i 25 | -------------------------------------------------------------------------------- /7. Binary Search on Answers/3. Leetcode 875 - Koko Eating Bananas/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | import math 3 | 4 | """ 5 | Time complexity -> O(N * log(max(piles))) 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | class Solution: 12 | def minEatingSpeed(self, piles: List[int], h: int) -> int: 13 | def totalHours(piles, hourly_rate): 14 | total = 0 15 | for i in range(len(piles)): 16 | total = total + math.ceil(piles[i] / hourly_rate) 17 | return total 18 | 19 | low = 1 20 | high = max(piles) 21 | ans = -1 22 | while low <= high: 23 | mid = (low + high) // 2 24 | total_hours = totalHours(piles, mid) 25 | if total_hours <= h: 26 | ans = mid 27 | high = mid - 1 28 | else: 29 | low = mid + 1 30 | return ans 31 | -------------------------------------------------------------------------------- /7. Binary Search on Answers/6. Leetcode 1011 - Capacity To Ship Packages Within D Days/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def find_days(self, weights, capacity): 6 | total_days = 1 7 | current_load = 0 8 | 9 | for weight in weights: 10 | if current_load + weight > capacity: 11 | total_days += 1 12 | current_load = 0 13 | current_load += weight 14 | 15 | return total_days 16 | 17 | def shipWithinDays(self, weights: List[int], days: int) -> int: 18 | start_weight = max(weights) 19 | end_weight = sum(weights) 20 | for w in range(start_weight, end_weight + 1): 21 | total_days_taken = self.find_days(weights, w) 22 | print(w, total_days_taken) 23 | if total_days_taken <= days: 24 | # return w 25 | pass 26 | 27 | 28 | obj = Solution() 29 | obj.shipWithinDays([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5) 30 | -------------------------------------------------------------------------------- /7. Binary Search on Answers/6. Leetcode 1011 - Capacity To Ship Packages Within D Days/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Solution: 5 | def find_days(self, weights, capacity): 6 | total_days = 1 7 | current_load = 0 8 | 9 | for weight in weights: 10 | if current_load + weight > capacity: 11 | total_days += 1 12 | current_load = 0 13 | current_load += weight 14 | 15 | return total_days 16 | 17 | def shipWithinDays(self, weights: List[int], days: int) -> int: 18 | # Do the below calculation using FOR loop 19 | low = max(weights) 20 | high = sum(weights) 21 | while low <= high: 22 | mid = (low + high) // 2 23 | numberOfDays = self.find_days(weights, mid) 24 | if numberOfDays <= days: 25 | high = mid - 1 26 | else: 27 | low = mid + 1 28 | return low 29 | -------------------------------------------------------------------------------- /7. Binary Search on Answers/7. Aggressive Cows/1. linear.py: -------------------------------------------------------------------------------- 1 | """Function to check if we can place 'cows' 2 | cows with at least 'dist' distance apart""" 3 | 4 | 5 | def canWePlace(stalls, dist, cows): 6 | # Size of array 7 | n = len(stalls) 8 | 9 | # Number of cows placed 10 | cntCows = 1 11 | 12 | # Position of last placed cow 13 | last = stalls[0] 14 | for i in range(1, n): 15 | if stalls[i] - last >= dist: 16 | # Place next cow 17 | cntCows += 1 18 | 19 | # Update the last location 20 | last = stalls[i] 21 | if cntCows >= cows: 22 | return True 23 | 24 | return False 25 | 26 | 27 | def aggressiveCows(stalls, k): 28 | # Size of list 29 | n = len(stalls) 30 | 31 | # Sort the nums 32 | stalls.sort() 33 | 34 | limit = stalls[-1] - stalls[0] 35 | for i in range(1, limit + 1): 36 | if not canWePlace(stalls, i, k): 37 | return i - 1 38 | 39 | # Return the answer 40 | return limit 41 | -------------------------------------------------------------------------------- /7. Binary Search on Answers/8. Allocate Books/1. linear.py: -------------------------------------------------------------------------------- 1 | def countStudents(nums, pages): 2 | n = len(nums) 3 | students = 1 4 | pagesStudent = 0 5 | 6 | for i in range(n): 7 | if pagesStudent + nums[i] <= pages: 8 | # Add pages to current student 9 | pagesStudent += nums[i] 10 | else: 11 | # Add pages to next student 12 | students += 1 13 | pagesStudent = nums[i] 14 | 15 | return students 16 | 17 | 18 | def findPages(arr: [int], n: int, m: int) -> int: 19 | n = len(arr) 20 | 21 | # Book allocation impossible 22 | if m > n: 23 | return -1 24 | 25 | # Calculate the range for binary search 26 | low = max(arr) 27 | high = sum(arr) 28 | 29 | # Linear search for minimum maximum pages 30 | for pages in range(low, high + 1): 31 | if countStudents(arr, pages) == m: 32 | return pages 33 | 34 | return low 35 | -------------------------------------------------------------------------------- /7. Binary Search on Answers/8. Allocate Books/2. binary-search.py: -------------------------------------------------------------------------------- 1 | def countStudents(nums, pages): 2 | n = len(nums) 3 | students = 1 4 | pagesStudent = 0 5 | 6 | for i in range(n): 7 | if pagesStudent + nums[i] <= pages: 8 | # Add pages to current student 9 | pagesStudent += nums[i] 10 | else: 11 | # Add pages to next student 12 | students += 1 13 | pagesStudent = nums[i] 14 | 15 | return students 16 | 17 | 18 | def findPages(arr: [int], n: int, m: int) -> int: 19 | n = len(arr) 20 | 21 | # Book allocation impossible 22 | if m > n: 23 | return -1 24 | 25 | # Calculate the range for binary search 26 | low = max(arr) 27 | high = sum(arr) 28 | 29 | while low <= high: 30 | mid = (low + high) // 2 31 | if countStudents(arr, mid) <= m: 32 | high = mid - 1 33 | else: 34 | low = mid + 1 35 | return low 36 | -------------------------------------------------------------------------------- /8. Singly Linked List/1. Introduction/1. Introduction To Linked List.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, val=0, next=None): 3 | self.val = val 4 | self.next = next 5 | 6 | 7 | # Do not change code above. 8 | 9 | 10 | def constructLL(arr: [int]) -> Node: 11 | n = len(arr) 12 | new_head = Node(arr[0]) 13 | current_node = new_head 14 | for i in range(1, n): 15 | new_node = Node(arr[i]) 16 | current_node.next = new_node 17 | current_node = new_node 18 | return new_head 19 | -------------------------------------------------------------------------------- /8. Singly Linked List/1. Introduction/2. Insert Node At The Beginning.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data=0, next=None): 3 | self.data = data 4 | self.next = next 5 | 6 | 7 | # Do not change code above. 8 | 9 | 10 | def insertAtFirst(list: Node, newValue: int) -> Node: 11 | new_node = Node(newValue) 12 | new_node.next = list 13 | return new_node 14 | -------------------------------------------------------------------------------- /8. Singly Linked List/1. Introduction/3. Delete Node Of Linked List.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, val=0, next=None): 3 | self.val = val 4 | self.next = next 5 | 6 | 7 | # Please do not change code above. 8 | 9 | 10 | def deleteLast(list: Node) -> Node: 11 | head = list 12 | current_node = head 13 | prev = None 14 | while current_node.next: 15 | prev = current_node 16 | current_node = current_node.next 17 | prev.next = None 18 | return head 19 | -------------------------------------------------------------------------------- /8. Singly Linked List/1. Introduction/4. Count nodes of linked list.py: -------------------------------------------------------------------------------- 1 | """ 2 | Following is the structure of the Node class already defined. 3 | 4 | class Node: 5 | def __init__(self, data): 6 | self.data = data 7 | self.next = None 8 | 9 | """ 10 | 11 | 12 | def length(head): 13 | count = 0 14 | current = head 15 | while current: 16 | count += 1 17 | current = current.next 18 | return count 19 | -------------------------------------------------------------------------------- /8. Singly Linked List/1. Introduction/5. Search in a Linked List.py: -------------------------------------------------------------------------------- 1 | """ 2 | Following is the structure of the Node class already defined. 3 | 4 | class Node: 5 | def __init__(self, data): 6 | self.data = data 7 | self.next = None 8 | 9 | """ 10 | 11 | 12 | def searchInLinkedList(head, k): 13 | current = head 14 | while current: 15 | if current.data == k: 16 | return 1 17 | current = current.next 18 | return 0 19 | -------------------------------------------------------------------------------- /8. Singly Linked List/2. Easy/1. Leetcode 876 - Middle of the Linked List/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | """ 4 | Time complexity -> O(N + N/2) 5 | N is the number of nodes 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | # Definition for singly-linked list. 12 | class ListNode: 13 | def __init__(self, val=0, next=None): 14 | self.val = val 15 | self.next = next 16 | 17 | 18 | class Solution: 19 | def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]: 20 | n = 0 21 | temp = head 22 | while temp: 23 | n += 1 24 | temp = temp.next 25 | temp = head 26 | for i in range(n // 2): 27 | temp = temp.next 28 | return temp 29 | -------------------------------------------------------------------------------- /8. Singly Linked List/2. Easy/1. Leetcode 876 - Middle of the Linked List/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) 3 | N is the number of nodes 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | # Definition for singly-linked list. 10 | class ListNode: 11 | def __init__(self, val=0, next=None): 12 | self.val = val 13 | self.next = next 14 | 15 | 16 | # Also known as Tortoise-Hare-Approach 17 | class Solution: 18 | def middleNode(self, head: ListNode) -> ListNode: 19 | slow = head 20 | fast = head 21 | while fast and fast.next: 22 | slow = slow.next 23 | fast = fast.next.next 24 | return slow 25 | -------------------------------------------------------------------------------- /8. Singly Linked List/2. Easy/2. Leetcode 206 - Reverse Linked List/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | """ 4 | Time complexity -> O(2N) -> O(N) 5 | N is the number of nodes 6 | 7 | Space complexity -> O(N) 8 | We use a stack to store the values of the linked list, and in the worst case, 9 | the stack will have all N values, ie. storing the complete linked list. 10 | """ 11 | 12 | 13 | # Definition for singly-linked list. 14 | class ListNode: 15 | def __init__(self, val=0, next=None): 16 | self.val = val 17 | self.next = next 18 | 19 | 20 | class Solution: 21 | def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: 22 | temp = head 23 | stack = [] 24 | while temp is not None: 25 | stack.append(temp.val) 26 | temp = temp.next 27 | temp = head 28 | 29 | while temp is not None: 30 | temp.val = stack.pop() 31 | temp = temp.next 32 | 33 | return head 34 | -------------------------------------------------------------------------------- /8. Singly Linked List/2. Easy/2. Leetcode 206 - Reverse Linked List/iterative-approach.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | """ 4 | Time complexity -> O(N) 5 | O(N) The code traverses the entire linked list once, 6 | where 'n' is the number of nodes in the list. 7 | This traversal has a linear time complexity, O(n). 8 | 9 | Space complexity -> O(1) 10 | The code uses only a constant amount of additional space, 11 | regardless of the linked list's length. 12 | """ 13 | 14 | 15 | # Definition for singly-linked list. 16 | class ListNode: 17 | def __init__(self, val=0, next=None): 18 | self.val = val 19 | self.next = next 20 | 21 | 22 | class Solution: 23 | def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: 24 | temp = head 25 | prev = None 26 | while temp is not None: 27 | front = temp.next 28 | temp.next = prev 29 | prev = temp 30 | temp = front 31 | return prev 32 | -------------------------------------------------------------------------------- /8. Singly Linked List/2. Easy/3. Leetcode 141 - Linked List Cycle/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | """ 4 | Time complexity -> O(N) 5 | N is the number of nodes 6 | 7 | Space complexity -> O(N) 8 | The code uses a hashmap/dictionary to store encountered nodes, 9 | which can take up to O(N) additional space, 10 | where 'n' is the number of nodes in the list. 11 | """ 12 | 13 | 14 | # Definition for singly-linked list. 15 | class ListNode: 16 | def __init__(self, x): 17 | self.val = x 18 | self.next = None 19 | 20 | 21 | class Solution: 22 | def hasCycle(self, head: Optional[ListNode]) -> bool: 23 | temp = head 24 | node_set = set() 25 | while temp is not None: 26 | if temp in node_set: 27 | return True 28 | node_set.add(temp) 29 | temp = temp.next 30 | return False 31 | -------------------------------------------------------------------------------- /8. Singly Linked List/2. Easy/3. Leetcode 141 - Linked List Cycle/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | """ 4 | Time complexity -> O(N) 5 | N is the number of nodes 6 | 7 | Space complexity -> O(1) 8 | The code uses only a constantamount of additionalspace, 9 | regardless of the linked list's length. 10 | """ 11 | 12 | 13 | # Definition for singly-linked list. 14 | class ListNode: 15 | def __init__(self, x): 16 | self.val = x 17 | self.next = None 18 | 19 | 20 | class Solution: 21 | def hasCycle(self, head: Optional[ListNode]) -> bool: 22 | slow = head 23 | fast = head 24 | 25 | while fast is not None and fast.next is not None: 26 | slow = slow.next 27 | fast = fast.next.next 28 | if slow == fast: 29 | return True 30 | 31 | return False 32 | -------------------------------------------------------------------------------- /8. Singly Linked List/3. Medium/1. Leetcode 142 - Linked List Cycle II/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | """ 4 | Time complexity -> O(N) 5 | N is the number of nodes 6 | 7 | Space complexity -> O(N) 8 | The code uses a hashmap/dictionary/set to store encountered nodes, 9 | which can take up to O(N) additional space, 10 | where 'n' is the number of nodes in the list. 11 | """ 12 | 13 | 14 | # Definition for singly-linked list. 15 | class ListNode: 16 | def __init__(self, x): 17 | self.val = x 18 | self.next = None 19 | 20 | 21 | class Solution: 22 | def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: 23 | my_set = set() 24 | temp = head 25 | while temp is not None: 26 | if temp in my_set: 27 | return temp 28 | my_set.add(temp) 29 | temp = temp.next 30 | return None 31 | -------------------------------------------------------------------------------- /8. Singly Linked List/3. Medium/1. Leetcode 142 - Linked List Cycle II/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | """ 4 | Time complexity -> O(N) 5 | N is the number of nodes 6 | 7 | Space complexity -> O(1) 8 | """ 9 | 10 | 11 | # Definition for singly-linked list. 12 | class ListNode: 13 | def __init__(self, x): 14 | self.val = x 15 | self.next = None 16 | 17 | 18 | class Solution: 19 | def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: 20 | slow = head 21 | fast = head 22 | while fast and fast.next: 23 | slow = slow.next 24 | fast = fast.next.next 25 | if slow == fast: 26 | slow = head 27 | while slow != fast: 28 | slow = slow.next 29 | fast = fast.next 30 | return slow 31 | return None 32 | -------------------------------------------------------------------------------- /8. Singly Linked List/3. Medium/11. Add 2 numbers in LL/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | # Definition for singly-linked list. 5 | class ListNode: 6 | def __init__(self, val=0, next=None): 7 | self.val = val 8 | self.next = next 9 | 10 | 11 | class Solution: 12 | def addTwoNumbers( 13 | self, l1: Optional[ListNode], l2: Optional[ListNode] 14 | ) -> Optional[ListNode]: 15 | result = ListNode(0) 16 | temp = result 17 | carry = 0 18 | while (l1 != None and l2 != None) or carry: 19 | total = 0 20 | if l1 != None: 21 | total += l1.val 22 | l1 = l1.next 23 | if l2 != None: 24 | total += l2.val 25 | l2 = l2.next 26 | total += carry 27 | if total > 9: 28 | carry = 1 29 | else: 30 | carry = 0 31 | temp.next = ListNode(total % 10) 32 | temp = temp.next 33 | return result.next 34 | -------------------------------------------------------------------------------- /8. Singly Linked List/3. Medium/2. Find length of Loop/brute-force.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) 3 | N is the number of nodes 4 | 5 | Space complexity -> O(N) 6 | The code uses a hashmap/dictionary/set to store encountered nodes, 7 | which can take up to O(N) additional space, 8 | where 'n' is the number of nodes in the list. 9 | """ 10 | 11 | 12 | class Node: 13 | def __init__(self, data=0, next=None): 14 | self.val = data 15 | self.next = next 16 | 17 | 18 | # Please do not change code above. 19 | 20 | 21 | def lengthOfLoop(head: Node) -> int: 22 | my_dict = dict() 23 | temp = head 24 | travel = 0 25 | while temp is not None: 26 | if temp in my_dict: 27 | return travel - my_dict[temp] 28 | my_dict[temp] = travel 29 | travel += 1 30 | temp = temp.next 31 | return 0 32 | -------------------------------------------------------------------------------- /8. Singly Linked List/3. Medium/2. Find length of Loop/optimal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Time complexity -> O(N) 3 | N is the number of nodes 4 | 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | class Node: 10 | def __init__(self, data=0, next=None): 11 | self.val = data 12 | self.next = next 13 | 14 | 15 | # Please do not change code above. 16 | 17 | 18 | def lengthOfLoop(head: Node) -> int: 19 | slow = head 20 | fast = head 21 | while fast and fast.next: 22 | slow = slow.next 23 | fast = fast.next.next 24 | if slow == fast: 25 | slow = slow.next 26 | count = 1 27 | while slow is not fast: 28 | count += 1 29 | slow = slow.next 30 | return count 31 | return 0 32 | -------------------------------------------------------------------------------- /8. Singly Linked List/3. Medium/3. Odd and Even List/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | # Definition for singly-linked list. 5 | class ListNode: 6 | def __init__(self, val=0, next=None): 7 | self.val = val 8 | self.next = next 9 | 10 | 11 | class Solution: 12 | def oddEvenList(self, head: Optional[ListNode]) -> Optional[ListNode]: 13 | if not head or not head.next: 14 | return head 15 | 16 | odd = head 17 | even = head.next 18 | even_head = even 19 | 20 | # Traverse the list, rearranging odd and even nodes 21 | while even and even.next: 22 | odd.next = odd.next.next 23 | odd = odd.next 24 | even.next = even.next.next 25 | even = even.next 26 | 27 | # Append the even list to the end of the odd list 28 | odd.next = even_head 29 | 30 | return head 31 | -------------------------------------------------------------------------------- /8. Singly Linked List/3. Medium/4. Leetcode 234 - Palindrome Linked List/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | """ 4 | Time complexity -> O(2N) 5 | N is the number of nodes 6 | 7 | Space complexity -> O(N) 8 | The code uses a stack to store encountered nodes 9 | where 'n' is the number of nodes in the list. 10 | """ 11 | 12 | 13 | # Definition for singly-linked list. 14 | class ListNode: 15 | def __init__(self, val=0, next=None): 16 | self.val = val 17 | self.next = next 18 | 19 | 20 | class Solution: 21 | def isPalindrome(self, head: Optional[ListNode]) -> bool: 22 | temp = head 23 | stack = [] 24 | while temp is not None: 25 | stack.append(temp.val) 26 | temp = temp.next 27 | temp = head 28 | while temp is not None: 29 | if temp.val != stack.pop(): 30 | return False 31 | temp = temp.next 32 | return True 33 | -------------------------------------------------------------------------------- /8. Singly Linked List/3. Medium/5. Leetcode 19 - Remove Nth Node From End of List/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | """ 4 | Time complexity -> O(N) 5 | O(N) since the fast pointer will traverse the entire linked list, 6 | where N is the length of the linked list. 7 | 8 | Space complexity -> O(1) 9 | """ 10 | 11 | 12 | # Definition for singly-linked list. 13 | class ListNode: 14 | def __init__(self, val=0, next=None): 15 | self.val = val 16 | self.next = next 17 | 18 | 19 | class Solution: 20 | def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: 21 | fastp = head 22 | slowp = head 23 | 24 | for _ in range(n): 25 | fastp = fastp.next 26 | 27 | if fastp is None: 28 | return head.next 29 | 30 | while fastp.next is not None: 31 | fastp = fastp.next 32 | slowp = slowp.next 33 | 34 | slowp.next = slowp.next.next 35 | return head 36 | -------------------------------------------------------------------------------- /8. Singly Linked List/3. Medium/6. Leetcode 2095 - Delete the Middle Node of a Linked List/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | """ 4 | Time complexity -> O(N/2) 5 | Space complexity -> O(1) 6 | """ 7 | 8 | 9 | # Definition for singly-linked list. 10 | class ListNode: 11 | def __init__(self, val=0, next=None): 12 | self.val = val 13 | self.next = next 14 | 15 | 16 | # Tortoise Hare Approach (slightly changed) 17 | class Solution: 18 | def deleteMiddle(self, head: Optional[ListNode]) -> Optional[ListNode]: 19 | if not head.next: 20 | return None 21 | slow = head 22 | fast = head 23 | fast = fast.next.next 24 | while fast and fast.next: 25 | slow = slow.next 26 | fast = fast.next.next 27 | slow.next = slow.next.next 28 | return head 29 | -------------------------------------------------------------------------------- /8. Singly Linked List/3. Medium/8. Sort a linked list of 0s, 1s and 2s/brute-force.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | # Function to sort a linked list of 0s, 1s and 2s. 3 | def segregate(self, head): 4 | if head is None or head.next is None: 5 | return head 6 | temp = head 7 | cnt0, cnt1, cnt2 = 0, 0, 0 8 | while temp: 9 | if temp.data == 0: 10 | cnt0 += 1 11 | elif temp.data == 1: 12 | cnt1 += 1 13 | else: 14 | cnt2 += 1 15 | temp = temp.next 16 | temp = head 17 | for _ in range(cnt0): 18 | temp.data = 0 19 | temp = temp.next 20 | for _ in range(cnt1): 21 | temp.data = 1 22 | temp = temp.next 23 | for _ in range(cnt2): 24 | temp.data = 2 25 | temp = temp.next 26 | return head 27 | -------------------------------------------------------------------------------- /8. Singly Linked List/3. Medium/9. Intersection of Two Linked Lists/brute-force.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | # Definition for singly-linked list. 5 | class ListNode: 6 | def __init__(self, x): 7 | self.val = x 8 | self.next = None 9 | 10 | 11 | class Solution: 12 | def getIntersectionNode( 13 | self, headA: ListNode, headB: ListNode 14 | ) -> Optional[ListNode]: 15 | all_nodes = set() 16 | temp = headA 17 | while temp: 18 | all_nodes.add(temp) 19 | temp = temp.next 20 | 21 | temp = headB 22 | while temp: 23 | if temp in all_nodes: 24 | return temp 25 | temp = temp.next 26 | return None 27 | -------------------------------------------------------------------------------- /8. Singly Linked List/3. Medium/9. Intersection of Two Linked Lists/optimal.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | # Definition for singly-linked list. 5 | class ListNode: 6 | def __init__(self, x): 7 | self.val = x 8 | self.next = None 9 | 10 | 11 | class Solution: 12 | def getIntersectionNode( 13 | self, headA: ListNode, headB: ListNode 14 | ) -> Optional[ListNode]: 15 | if not headA or not headB: 16 | return None 17 | 18 | temp1, temp2 = headA, headB 19 | 20 | while temp1 != temp2: 21 | if temp1 is not None: 22 | temp1 = temp1.next 23 | else: 24 | temp1 = headB 25 | if temp2 is not None: 26 | temp2 = temp2.next 27 | else: 28 | temp2 = headA 29 | 30 | return temp1 31 | -------------------------------------------------------------------------------- /9. Doubly Linked List/Medium/1. Delete all occurrences of a given key in a doubly linked list/optimal2.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data): 3 | self.data = data 4 | self.next = None 5 | self.prev = None 6 | 7 | 8 | def deleteAllOccurrences(head: Node, k: int) -> Node: 9 | # Delete occurrences from the head 10 | while head and head.data == k: 11 | next_node = head.next 12 | if next_node: 13 | next_node.prev = None 14 | head = next_node 15 | 16 | current = head 17 | 18 | while current: 19 | if current.data == k: 20 | if current.prev: 21 | current.prev.next = current.next 22 | if current.next: 23 | current.next.prev = current.prev 24 | current = current.next 25 | 26 | return head 27 | -------------------------------------------------------------------------------- /9. Doubly Linked List/Medium/2. Find pairs with given sum in doubly linked list/better.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data=0, next=None, prev=None): 3 | self.data = data 4 | self.next = next 5 | self.prev = prev 6 | 7 | 8 | """ 9 | Time complexity -> O(N) 10 | N is the number of nodes 11 | 12 | Space complexity -> O(N) 13 | """ 14 | 15 | 16 | def findPairs(head: Node, k: int) -> [[int]]: 17 | 18 | my_set = set() 19 | temp = head 20 | result = [] 21 | while temp: 22 | if k - temp.data in my_set: 23 | result.append([k - temp.data, temp.data]) 24 | my_set.add(temp.data) 25 | temp = temp.next 26 | return result 27 | -------------------------------------------------------------------------------- /9. Doubly Linked List/Medium/2. Find pairs with given sum in doubly linked list/brute.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data=0, next=None, prev=None): 3 | self.data = data 4 | self.next = next 5 | self.prev = prev 6 | 7 | 8 | """ 9 | Time complexity -> O(N^2) 10 | N is the number of nodes 11 | 12 | Space complexity -> O(1) 13 | """ 14 | 15 | 16 | def findPairs(head: Node, k: int) -> [[int]]: 17 | temp1 = head 18 | result = [] 19 | while temp1.next is not None: 20 | temp2 = temp1.next 21 | while temp2 is not None: 22 | if temp1.data + temp2.data == k: 23 | result.append([temp1.data, temp2.data]) 24 | temp2 = temp2.next 25 | temp1 = temp1.next 26 | return result 27 | -------------------------------------------------------------------------------- /9. Doubly Linked List/Medium/2. Find pairs with given sum in doubly linked list/optimal.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data=0, next=None, prev=None): 3 | self.data = data 4 | self.next = next 5 | self.prev = prev 6 | 7 | 8 | """ 9 | Time complexity -> O(N/2) 10 | N is the number of nodes 11 | 12 | Space complexity -> O(1) 13 | """ 14 | 15 | 16 | def findPairs(head: Node, k: int) -> [[int]]: 17 | result = [] 18 | left = head 19 | right = head 20 | while right.next: 21 | right = right.next 22 | while left and right and left.data < right.data: 23 | total = left.data + right.data 24 | if total == k: 25 | result.append([left.data, right.data]) 26 | right = right.prev 27 | left = left.next 28 | elif total > k: 29 | right = right.prev 30 | else: 31 | left = left.next 32 | return result 33 | -------------------------------------------------------------------------------- /9. Doubly Linked List/Medium/3. Remove duplicates from a sorted Doubly Linked List/optimal.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data=0, next=None, prev=None): 3 | self.data = data 4 | self.next = next 5 | self.prev = prev 6 | 7 | 8 | """ 9 | Time complexity -> O(N) 10 | N is the number of nodes 11 | 12 | Space complexity -> O(1) 13 | """ 14 | 15 | 16 | def removeDuplicates(self, head): 17 | cur = head 18 | while cur: 19 | if cur.prev and cur.prev.data == cur.data: 20 | if cur.prev == head: 21 | cur.prev = None 22 | head = cur 23 | else: 24 | cur.prev.prev.next = cur 25 | cur.prev = cur.prev.prev 26 | 27 | cur = cur.next 28 | return head 29 | -------------------------------------------------------------------------------- /9. Doubly Linked List/Medium/4. Reverse a Doubly Linked List/brute-force.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def reverseDLL(self, head): 3 | if not head: 4 | return None 5 | 6 | stack = [] 7 | current = head 8 | 9 | # First pass: push all node data onto stack 10 | while current: 11 | stack.append(current.data) 12 | current = current.next 13 | 14 | # Second pass: pop from stack to reassign data in reverse 15 | current = head 16 | while current: 17 | current.data = stack.pop() 18 | current = current.next 19 | 20 | return head 21 | -------------------------------------------------------------------------------- /9. Doubly Linked List/Medium/4. Reverse a Doubly Linked List/optimal.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def reverseDLL(self, head): 3 | # Edge cases: if list is empty or has one node, just return head 4 | if not head or not head.next: 5 | return head 6 | 7 | prev = None 8 | current = head 9 | 10 | # Traverse through the list 11 | while current: 12 | # Store the next node 13 | front = current.next 14 | 15 | # Reverse current node's pointers 16 | current.next = prev 17 | current.prev = front 18 | 19 | # Move the prev pointer forward (to current) 20 | prev = current 21 | 22 | # Move current pointer to the originally 'next' node 23 | current = front 24 | 25 | # 'prev' will be pointing to the new head of the reversed list 26 | return prev 27 | --------------------------------------------------------------------------------