├── .gitignore ├── arrays ├── digits-of-a-number.py ├── leetcode │ ├── analyze-user-website-visit-pattern-1152.py │ ├── can-place-flowers-605.py │ ├── consecutive-numbers-sum-829.py │ ├── grid │ │ ├── minimum-knight-moves-1197.py │ │ └── two-pointer │ │ │ ├── determine-whether-matrix-can-be-obtained-by-rotation-1886.py │ │ │ └── rotate-image-48.py │ ├── next-greater-element-III-556.py │ ├── prefix-sum │ │ └── range-addition-370.py │ ├── single-number-136.py │ ├── trapping-rain-water-42.py │ └── two-pointer │ │ ├── 2sum │ │ ├── two-sum-II-input-array-is-sorted-167.py │ │ └── two-sum-less-than-k-1099.py │ │ ├── 3sum │ │ ├── 3sum-15.py │ │ ├── 3sum-closest-16.py │ │ └── 3sum-smaller-259.py │ │ ├── 4sum │ │ └── 4sum-18.py │ │ ├── merge-sorted-array-88.py │ │ ├── product-of-array-except-self-238.py │ │ ├── remove-duplicates-from-sorted-array-26.py │ │ ├── remove-duplicates-from-sorted-array-II-80.py │ │ └── rotate-array-189.py ├── practice.py ├── rotate-matrix │ ├── rotate-matrix-90-anti-clockwise.py │ └── rotate-matrix-90-clockwise.py └── two-sorted-arrays │ ├── intersection-and-union-duplicates.py │ ├── intersection-and-union.py │ └── merge.py ├── binary-search ├── closest-sorted-array-value.py ├── leetcode │ ├── binary-search-704.py │ ├── first-and-last │ │ ├── count-elements<=target-in-sorted-array.py │ │ ├── count-elements>=target-in-sorted-array.py │ │ ├── find-first-and-last-position-of-element-in-sorted-array-34.py │ │ ├── first-bad-version-278.py │ │ ├── first-strictly-greater-element.py │ │ └── first-strictly-smaller-element.py │ ├── grid │ │ └── search-a-2D-matrix-74.py │ ├── math │ │ ├── sqrt(x)-69.py │ │ └── valid-perfect-square-367.py │ ├── peak │ │ ├── find-peak-element-162.py │ │ └── peak-index-in-a-mountain-array-852.py │ ├── range │ │ ├── allocate-minimum-number-of-pages.py │ │ ├── capacity-to-ship-packages-within-d-days-1011.py │ │ ├── find-kth-element-in-a-sorted-array.py │ │ ├── find-kth-smallest-pair-distance-719.py │ │ └── split-array-largest-sum-410.py │ ├── rotated-sorted-array │ │ ├── find-minimum-in-rotated-sorted-array-153.py │ │ ├── find-minimum-in-rotated-sorted-array-II-154-(duplicates).py │ │ ├── pivot-index.py │ │ ├── search-in-rotated-sorted-array-33.py │ │ └── search-in-rotated-sorted-array-II-81-(duplicates).py │ ├── search-in-a-sorted-array-of-unknown-size-702.py │ ├── search-insert-position │ │ ├── find-k-closest-elements-658.py │ │ ├── find-smallest-letter-greater-than-target-744.py │ │ ├── missing-number │ │ │ ├── kth-missing-positive-number-1539.py │ │ │ └── missing-element-in-sorted-array-1060.py │ │ └── search-insert-position-35.py │ └── single-element-in-a-sorted-array-540.py ├── min-max-key.py ├── minimum-difference-pair.py └── smallest-missing-number-in-sorted-array.py ├── companies ├── leetcode.py ├── links.py ├── solve-1.py └── solve-2.py ├── cyclic-sort ├── cyclic-sort.py ├── first-k-missing-positive-numbers.py ├── leetcode │ ├── duplicate │ │ ├── find-all-duplicates-in-an-array-442.py │ │ └── find-the-duplicate-number-287.py │ ├── missing │ │ ├── find-all-numbers-disappeared-in-an-array-448.py │ │ ├── first-missing-positive-41.py │ │ └── missing-number-268.py │ └── set-mismatch-645.py └── missing-number.py ├── design ├── LRU-cache-(OrderedDict).py ├── explore.py └── leetcode │ ├── LFU-cache-460.py │ ├── LRU-cache-146.py │ ├── design-tic-tac-toe-348.py │ └── insert-delete-get-random-O(1)-380.py ├── dynamic-programming ├── count-binary-strings │ ├── length-n-with-no-consecutive-ones.py │ └── length-n.py ├── digit-dynamic-programming │ ├── atcoder │ │ ├── count-numbers-till-n-having-exactly-k-non-zero-digits-(e-almost-everywhere-zero).py │ │ └── max-sum-of-digits-in-numbers-till-n-(digit-sum-2).py │ ├── leetcode │ │ ├── digit-count-in-range-1067.py │ │ ├── non-negative-integers-without-consecutive-ones-600.py │ │ └── number-of-digit-one-233.py │ ├── practice │ │ ├── count-n-digit-numbers.py │ │ ├── get-n-digit-numbers.py │ │ ├── sum-of-digits-of-n-digit-numbers.py │ │ └── sum-of-digits-of-numbers-till-n.py │ ├── spoj │ │ └── sum-of-digits-of-numbers-in-range-(digit-sum).py │ └── template │ │ ├── count-binary-numbers-till-n.py │ │ └── count-decimal-numbers-till-n.py ├── explore.py ├── first-and-second-minimum.py ├── leetcode │ ├── buy-and-sell-stock │ │ ├── best-time-to-buy-and-sell-stock-121-(1-transaction).py │ │ ├── best-time-to-buy-and-sell-stock-II-122-(∞ transactions).py │ │ ├── best-time-to-buy-and-sell-stock-III-123-(2 transactions).py │ │ ├── best-time-to-buy-and-sell-stock-IV-188-(k-transactions).py │ │ ├── best-time-to-buy-and-sell-stock-with-cooldown-309-(∞ transactions).py │ │ └── best-time-to-buy-and-sell-stock-with-transaction-fee-714-(∞ transactions).py │ ├── flip-string-to-monotone-string-926.py │ ├── house-robber │ │ ├── house-robber-198.py │ │ ├── house-robber-II-213.py │ │ └── house-robber-III-337.py │ ├── paint-fence-276.py │ ├── paint-house │ │ ├── count.py │ │ ├── paint-house-256.py │ │ └── paint-house-II-265.py │ ├── path-with-maximum-gold-1219.py │ ├── regular-expression-matching-10.py │ ├── remove-adjacent-duplicates.py │ ├── shape │ │ ├── largest-plus-sign-764.py │ │ └── maximal-square-221.py │ ├── string-equality │ │ ├── delete-operation-for-two-strings-583.py │ │ ├── edit-distance-72.py │ │ ├── interleaving-string-97.py │ │ ├── minimum-ascii-delete-sum-for-two-strings-712.py │ │ └── minimum-deletions-and-insertions-to-transform-a-string-into-another.py │ ├── subarray │ │ ├── kadane-algorithm.py │ │ ├── longest-common-substring.py │ │ ├── longest-continuous-increasing-subsequence-674-(longest-increasing-subarray).py │ │ ├── max-sum-of-subarrays-from-start.py │ │ ├── maximum-length-of-repeated-subarray-718.py │ │ ├── maximum-product-subarray-152.py │ │ ├── maximum-subarray-53.py │ │ ├── palindrome │ │ │ ├── longest-palindromic-substring-5.py │ │ │ ├── longest-palindromic-substring-length.py │ │ │ ├── minimum-deletions-to-make-a-string-palindrome.py │ │ │ ├── minimum-insertion-steps-to-make-a-string-palindrome-1312.py │ │ │ └── palindromic-substrings-647.py │ │ └── subarrays.py │ ├── subsequence │ │ └── longest │ │ │ ├── increasing │ │ │ ├── increasing-triplet-subsequence-334.py │ │ │ ├── longest-increasing-subsequence-300.py │ │ │ ├── minimum-deletions-to-make-a-sequence-sorted.py │ │ │ └── number-of-longest-increasing-subsequence-673.py │ │ │ ├── longest-common-subsequence-1143.py │ │ │ └── longest-palindromic-subsequence-516.py │ └── wildcard-matching-44.py └── n-places-m-choices │ ├── 2-consecutive-choices-are-not-same.py │ └── 3-consecutive-choices-are-not-same.py ├── fun.py ├── generic ├── bottom-up │ ├── height.py │ ├── maximum-value-leaf-node.py │ ├── nodes-at-distance-k-from-root.py │ ├── root-to-leaf-max-cost-path.py │ ├── root-to-leaf-min-cost-path.py │ ├── root-to-leaf-paths-sum.py │ ├── root-to-leaf-paths.py │ ├── root-to-node-path.py │ ├── root-to-node-paths-at-k-distance.py │ └── root-to-node-paths-of-size-k.py ├── display.py ├── preorder.py ├── top-down │ ├── preorder.py │ ├── print-all-root-to-node-paths.py │ ├── root-to-leaf-paths.py │ └── root-to-node-path.py └── util.py ├── graphs ├── leetcode │ ├── all-paths-from-source-to-target-797.py │ ├── bipartite-785.py │ ├── connected-components │ │ ├── connecting-cities-with-minimum-cost-1135.py │ │ ├── max-area-of-island-695.py │ │ ├── number-of-connected-components-in-an-undirected-graph-323.py │ │ ├── number-of-islands-200.py │ │ ├── number-of-operations-to-make-network-connected-1319.py │ │ └── number-of-provinces-547.py │ ├── maze │ │ ├── maze-490.py │ │ └── maze-505.py │ ├── practice.py │ ├── reconstruct-itinerary-332.py │ ├── rotting-oranges-994.py │ └── word-ladder │ │ ├── word-ladder-127.py │ │ └── word-ladder-II-126.py ├── practice │ ├── bfs │ │ ├── bfs-in-grid.py │ │ └── bfs.py │ ├── count-connected-components.py │ ├── cycle │ │ ├── directed.py │ │ └── undirected.py │ ├── dfs │ │ ├── dfs-in-grid.py │ │ ├── dfs.py │ │ └── preorder.py │ ├── get-connected-components.py │ ├── hamiltonian-path-and-cycle.py │ ├── has-path.py │ ├── heap │ │ ├── minimum-spanning-tree.py │ │ └── src-to-all-vertices-min-weight-path.py │ ├── iterative-traversal.py │ ├── knights-tour │ │ ├── count-possible-moves.py │ │ ├── src-dest │ │ │ ├── count-paths.py │ │ │ ├── min-jumps-bfs.py │ │ │ └── min-jumps-dfs.py │ │ └── visit-all │ │ │ ├── count-all-configs.py │ │ │ ├── print-all-configs.py │ │ │ └── print-one-config.py │ ├── perfect-friends.py │ ├── spread-of-infection.py │ ├── src-dest │ │ ├── bfs │ │ │ ├── src-to-dest-path-bfs.py │ │ │ ├── src-to-dest-paths-bfs.py │ │ │ └── src-to-dest-shortest-path-length.py │ │ ├── bottom-up │ │ │ ├── all-src-to-dest-paths.py │ │ │ ├── count-src-to-dest-paths.py │ │ │ ├── src-to-dest-min-cost-path-weighted-graph.py │ │ │ ├── src-to-dest-path.py │ │ │ └── src-to-dest-shortest-path-length.py │ │ ├── src-to-dest-multisolver.py │ │ └── top-down │ │ │ ├── get-paths-2.py │ │ │ ├── get-paths.py │ │ │ └── print │ │ │ ├── print-path.py │ │ │ ├── print-paths-2.py │ │ │ └── print-paths.py │ ├── topological-sort.py │ ├── undirected │ │ ├── is-connected.py │ │ └── is-tree.py │ └── visit-all │ │ ├── print-all-configs.py │ │ └── print-one-config.py └── util.py ├── greedy ├── activity-selection.py └── leetcode │ ├── grid │ └── search-a-2D-matrix-II-240.py │ ├── maximum-units-on-a-truck-1710.py │ ├── meeting-rooms-252.py │ ├── merge-intervals-56.py │ ├── minimum │ ├── minimum-deletion-cost-to-avoid-repeating-letters-1578.py │ ├── minimum-deletions-to-make-character-frequencies-unique-1647.py │ ├── minimum-difference-between-largest-and-smallest-values-in-three-moves-1509.py │ └── minimum-number-of-arrows-to-burst-balloons-452.py │ ├── popular │ ├── container-with-most-water-11.py │ ├── find-the-celebrity-277.py │ └── gas-station-134.py │ ├── reorganize-string-767.py │ ├── task-scheduler-621.py │ └── time │ ├── car-pooling-1094.py │ └── meeting-rooms-II-253.py ├── grokking ├── 15.k-way-merge │ ├── k-smallest-elements-in-m-sorted-arrays.py │ ├── kth-smallest-element-in-2-sorted-arrays.py │ ├── kth-smallest-element-in-m-sorted-arrays.py │ └── leetcode │ │ ├── find-k-pairs-with-smallest-sums-373.py │ │ ├── kth-smallest-element-in-a-sorted-matrix-378.py │ │ ├── median-of-two-sorted-arrays-4.py │ │ └── smallest-range-covering-elements-from-k-lists-632.py └── 2.sliding-window │ └── leetcode │ └── sliding-window-maximum-239.py ├── hashmap ├── find-itinerary-from-tickets.py ├── highest-frequency-character.py ├── largest-subarray-with-contiguous-elements-(distinct elements).py ├── largest-subarray-with-contiguous-elements.py ├── leetcode │ ├── 2-arrays │ │ ├── intersection-of-two-arrays-349.py │ │ └── intersection-of-two-arrays-II-350.py │ ├── 2sum │ │ ├── count-pairs-with-given-sum.py │ │ └── two-sum-1.py │ ├── 4sum │ │ └── 4sum-II-454.py │ ├── anagrams │ │ ├── find-anagram-mappings-760.py │ │ ├── group-anagrams-49.py │ │ └── valid-anagram-242.py │ ├── check-if-array-pairs-are-divisible-by-k-1497.py │ ├── count-unique-characters-of-all-substrings-of-a-given-string-828.py │ ├── first-unique-character-387.py │ ├── fixed-window │ │ ├── count-distinct-elements-in-every-window-of-size-k.py │ │ ├── find-all-anagrams-in-a-string-438-(array-of-all-the-start-indices-of-p's-anagrams-in-s).py │ │ └── permutation-in-string-567-(check-if-s2-contains-a-permutation-of-s1).py │ ├── fraction-to-recurring-decimal-166.py │ ├── longest-consecutive-sequence-128.py │ ├── longest-substring-with-at-least-k-repeating-characters-395-(length).py │ ├── max-points-on-a-line-149.py │ ├── remove-duplicates-from-a-list-of-lists.py │ ├── sort-characters-by-frequency-451.py │ ├── subarray-sum │ │ ├── divisible │ │ │ ├── largest-subarray-with-sum-divisible-by-k-(length).py │ │ │ ├── pair-of-songs-with-total-durations-divisible-by-60-1010.py │ │ │ └── subarray-sums-divisible-by-k-974-(count).py │ │ ├── k │ │ │ ├── maximum-size-subarray-sum-equals-k-325-(length-of-the-largest-subarray-having-sum-k).py │ │ │ └── subarray-sum-equals-k-560-(count).py │ │ └── zero │ │ │ ├── contiguous-array │ │ │ ├── contiguous-array-525-(length-of-the-longest-subarray-with-an-equal-number-of-0-and-1).py │ │ │ └── count-subarrays-with-an-equal-number-of-0-and-1.py │ │ │ ├── count-subarrays-having-sum-zero.py │ │ │ └── length-of-the-largest-subarray-having-sum-zero.py │ └── subdomain-visit-count-811.py ├── longest-consecutive-sequence.py ├── number-of-employees-under-every-manager.py ├── practice.py ├── practice │ ├── counter.py │ ├── hashmap-using-list.py │ ├── hashmap.py │ ├── map-and-filter.py │ └── sort.py └── subarrays.py ├── heap ├── heap-operations.py ├── heap-sort.py ├── implementation │ ├── build.py │ └── priority-queue.py ├── leetcode │ ├── k-closest-points-to-origin-973.py │ ├── k-frequent │ │ ├── top-k-frequent-elements-347.py │ │ └── top-k-frequent-words-692.py │ ├── k-smallest-and-largest │ │ ├── k-largest-elements.py │ │ └── k-smallest-elements.py │ ├── kth-smallest-and-largest │ │ ├── kth-largest-element-in-a-stream-703.py │ │ ├── kth-largest-element-in-an-array-215.py │ │ └── kth-smallest-element-in-an-array.py │ ├── least-number-of-unique-integers-after-k-removals-1481.py │ ├── maximum-number-of-events-that-can-be-attended-1353.py │ ├── median-from-data-stream-295.py │ ├── merge-k-sorted │ │ ├── merge-k-sorted-arrays.py │ │ └── merge-k-sorted-lists-23.py │ ├── minimum │ │ ├── minimum-cost-to-connect-sticks-1167.py │ │ └── minimum-number-of-refueling-stops-871.py │ ├── the-kth-factor-of-n-1492.py │ └── util.py ├── median │ ├── median-from-data-stream.py │ └── median.py └── sort-k-sorted-array.py ├── library ├── binarytree │ ├── bst.py │ ├── heap.py │ └── tree.py ├── bisect-module.py ├── dataclasses │ └── explore.py └── sortedcontainers │ └── explore.py ├── linked ├── doubly-linked-list │ ├── doubly-linked-list-(tail-pointer).py │ └── doubly-linked-list.py ├── explore.py ├── leetcode │ ├── merge-two-sorted-lists-21.py │ ├── middle-of-the-linked-list-876.py │ ├── odd-even-linked-list-328.py │ ├── reverse-linked-list-206.py │ ├── rotate-list-61.py │ └── split-linked-list-in-parts-725.py └── util.py ├── park.py ├── practice.py ├── recursion ├── combinations-permutations │ ├── combinations-(count-and-get).py │ ├── combinations-infinite-supply-(count-and-get).py │ ├── minimum-elements-infinite-supply-(count-and-length-of-the-smallest-size-combination).py │ ├── permutations-(count-and-get).py │ └── permutations-infinite-supply-(count-and-get).py ├── combinations-tree │ ├── all-root-to-node-paths.py │ ├── preorder.py │ └── root-to-node-paths-of-size-k.py ├── explore.py ├── grid │ ├── maze-paths-with-jumps.py │ ├── n-queens │ │ ├── can-place-and-count-all-configs.py │ │ ├── print-all-configs.py │ │ └── print-and-get-all-queen-cells.py │ ├── right-and-down-path-with-minimum-cost.py │ ├── right-and-down.py │ └── right-down-left-up.py ├── leetcode │ ├── cherry-pickup-741.py │ ├── climbing-stairs │ │ ├── climbing-stairs-70-(count-permutations-infinite-supply).py │ │ └── min-cost-climbing-stairs-746.py │ ├── coin-change │ │ ├── coin-change-322-(minimum-elements-infinite-supply).py │ │ └── coin-change-II-518-(combinations-infinite-supply).py │ ├── combinations │ │ ├── combination-sum-39-(combinations-infinite-supply).py │ │ ├── combination-sum-II-40-(distinct-combinations).py │ │ ├── combination-sum-III-216-(combinations-size-k).py │ │ ├── combination-sum-IV-377-(count-permutations-infinite-supply).py │ │ └── combinations-77-(all-subsets-size-k).py │ ├── decode-ways │ │ ├── decode-ways-91.py │ │ └── get-decodings.py │ ├── dungeon-game-174.py │ ├── escape-a-large-maze-1036.py │ ├── fixed-moves │ │ ├── knight-dialer-935.py │ │ ├── knight-probability-in-chessboard-688.py │ │ └── out-of-boundary-paths-576.py │ ├── iterator │ │ ├── examples │ │ │ ├── linked-list-iterator.py │ │ │ ├── list-iterator.py │ │ │ ├── range-iterator.py │ │ │ ├── squares-iterator.py │ │ │ └── util.py │ │ ├── explore.py │ │ ├── flatten-2D-vector-251.py │ │ ├── flatten-nested-list-iterator-341.py │ │ ├── flatten-nested-list.py │ │ └── peeking-iterator-284.py │ ├── jump-game │ │ ├── jump-game-55-(can-reach-end).py │ │ ├── jump-game-II-45-(min-jumps-to-reach-end).py │ │ ├── jump-game-III-1306-(can-reach-end-with-forward-and-backward-jumps).py │ │ ├── jump-game-IV-1345-(min-jumps-to-reach-end-with-forward-and-backward-jumps).py │ │ ├── jump-game-V-1340-(max-indices-that-can-be-visited).py │ │ ├── jump-game-VI-1696-(max-cost-to-reach-target).py │ │ └── jump-game-VII-1871-(can-reach-end-with-fixed-jump-range).py │ ├── letter-combinations-of-a-phone-number-17.py │ ├── lexicographical-numbers-386.py │ ├── minimum-path-sum-64.py │ ├── n-queens │ │ ├── n-queens-51.py │ │ └── n-queens-52.py │ ├── palindrome-partitioning │ │ ├── count.py │ │ ├── find.py │ │ ├── palindrome-partitioning-131.py │ │ └── palindrome-partitioning-II-132.py │ ├── parentheses │ │ ├── generate-parentheses-22.py │ │ ├── generate-parentheses.py │ │ ├── minimum-remove-to-make-valid-parentheses-1249.py │ │ └── remove-invalid-parentheses-301.py │ ├── perfect-squares-279-(minimum-elements-infinite-supply).py │ ├── permutations │ │ ├── next-permutation-31.py │ │ ├── palindrome-permutation-266.py │ │ ├── palindrome-permutation-II-267.py │ │ ├── permutation-sequence-60.py │ │ ├── permutations-46.py │ │ └── permutations-II-47.py │ ├── prefix-and-suffix.py │ ├── remove-one-character.py │ ├── shortest-path-in-binary-matrix-1091-(shortest-path-length-from-src-to-dest-in-matrix-with-all-8-directions).py │ ├── subsets │ │ ├── knapsack.py │ │ ├── maximum-product.py │ │ ├── minimum-subset-sum-difference.py │ │ ├── partition-equal-subset-sum-416.py │ │ ├── partition-into-k-subsets │ │ │ ├── count-combinations-n-elements-k-non-empty-subsets.py │ │ │ └── partition-to-k-equal-sum-subsets-698.py │ │ ├── target-sum-494.py │ │ └── tug-of-war.py │ ├── sudoku │ │ ├── sudoku-solver-37.py │ │ └── valid-sudoku-36.py │ ├── unique-paths │ │ ├── unique-paths-62.py │ │ ├── unique-paths-II-63.py │ │ └── unique-paths-III-980.py │ ├── word-break │ │ ├── concatenated-words-472.py │ │ ├── count.py │ │ ├── find.py │ │ ├── word-break-139.py │ │ └── word-break-II-140.py │ ├── word-pattern │ │ ├── word-pattern-290.py │ │ └── word-pattern-II-291.py │ └── word-search │ │ └── word-search-79.py ├── non-adjacent-subsets │ ├── count.py │ ├── get.py │ └── max-sum-subset.py ├── subset-sum │ ├── equal │ │ ├── combinations-infinite-supply.py │ │ ├── combinations.py │ │ ├── count.py │ │ ├── distinct-combinations.py │ │ └── find.py │ └── knapsack │ │ ├── combinations.py │ │ ├── count.py │ │ └── knapsack.py └── subsets │ ├── leetcode │ ├── subsets-78.py │ └── subsets-II-90.py │ ├── subsets-backtracking.py │ ├── subsets-iterative.py │ └── subsets.py ├── sliding-window ├── count │ ├── count-substrings-with-at-most-k-distinct-characters.py │ ├── count-substrings-without-repeating-characters-(having-all-distinct-characters).py │ └── subarrays-with-k-different-integers-992-(count).py ├── longest-substring │ ├── longest-substring-with-at-most-k-distinct-characters-340.py │ ├── longest-substring-with-at-most-two-distinct-characters-159.py │ ├── longest-substring-with-k-distinct-characters.py │ └── longest-substring-without-repeating-characters-3.py ├── max-consecutive-ones │ ├── max-consecutive-ones-485-(length-of-the-longest-subarray-with-all-1s).py │ ├── max-consecutive-ones-II-487-(length-of-the-longest-subarray-with-all-1s-having-at-most-one-0).py │ └── max-consecutive-ones-III-1004-(length-of-the-longest-subarray-with-all-1s-having-at-most-k-0s).py └── minimize │ ├── degree-of-an-array-697.py │ ├── minimum-window-substring-76.py │ └── smallest-substring-of-a-string-containing-all-unique-characters-of-itself.py ├── sorting ├── count-inversions.py ├── leetcode │ ├── count-of-smaller-numbers-after-self-315.py │ ├── minimum-absolute-difference-1200.py │ ├── reorder-data-in-log-files-937.py │ └── sort-colors-75.py ├── merge-sort.py └── selection-sort.py ├── stacks ├── concept │ ├── monotonic │ │ ├── decreasing-stack.py │ │ └── increasing-stack.py │ ├── next-greater │ │ ├── next-greater-left.py │ │ └── next-greater-right.py │ └── next-smaller │ │ ├── next-smaller-left.py │ │ └── next-smaller-right.py ├── explore.py └── leetcode │ ├── 132-pattern-456.py │ ├── asteroid-collision-735.py │ ├── basic-calculator │ ├── basic-calculator-224.py │ ├── basic-calculator-II-227.py │ └── basic-calculator-III-772.py │ ├── design │ ├── design-a-stack-with-increment-operation-1381.py │ ├── max-stack-716.py │ └── min-stack-155.py │ ├── histogram │ ├── largest-rectangle-in-histogram-84.py │ ├── maximal-rectangle-85.py │ ├── maximum-score-of-good-subarray-1793.py │ └── sum-of-subarray-minimums-907.py │ ├── increasing │ ├── remove-duplicate-letters-316.py │ └── remove-k-digits-402.py │ ├── next-greater │ ├── daily-temperatures-739.py │ ├── next-greater-element-I-496.py │ └── next-greater-element-II-503.py │ ├── online-stock-span-901.py │ ├── shortest-unsorted-continuous-subarray-581.py │ └── string │ ├── backspace-string-compare-844.py │ ├── evaluate-reverse-polish-notation-150.py │ ├── find-duplicate-parenthesis-in-an-expression.py │ ├── remove-all-adjacent-duplicates-in-string-1047.py │ └── valid-parentheses-20.py ├── strings └── leetcode │ ├── count-binary-substrings-696.py │ ├── reverse-string │ ├── reverse-string-II-541.py │ └── two-pointer │ │ └── reverse-string-344.py │ ├── reverse-words-in-a-string │ └── two-pointer │ │ ├── reverse-words-in-a-string-151.py │ │ ├── reverse-words-in-a-string-II-186.py │ │ └── reverse-words-in-a-string-III-557.py │ └── two-pointer │ ├── remove-vowels-from-a-string-1119.py │ ├── reverse-vowels-of-a-string-345.py │ └── valid-palindrome-125.py ├── test.py ├── trees ├── binary │ ├── explore.py │ ├── leetcode │ │ ├── all-nodes-distance-k-in-binary-tree-863.py │ │ ├── binary-tree-paths-257.py │ │ ├── construct │ │ │ ├── construct-binary-tree-from-inorder-and-postorder-traversal-106.py │ │ │ └── construct-binary-tree-from-preorder-and-inorder-traversal-105.py │ │ ├── count-complete-tree-nodes-222.py │ │ ├── count-good-nodes-in-binary-tree-1448.py │ │ ├── dp │ │ │ ├── binary-tree-maximum-path-sum-124.py │ │ │ ├── diameter-of-binary-tree-543.py │ │ │ └── maximum-path-sum-between-two-leaves.py │ │ ├── flatten-binary-tree-to-linked-list-114.py │ │ ├── height │ │ │ ├── maximum-depth-of-binary-tree-104.py │ │ │ └── minimum-depth-of-binary-tree-111.py │ │ ├── lca │ │ │ ├── lowest-common-ancestor-of-a-binary-tree-236.py │ │ │ └── lowest-common-ancestor-of-a-binary-tree-II-1644.py │ │ ├── path-sum │ │ │ ├── path-sum-112.py │ │ │ ├── path-sum-II-113.py │ │ │ └── path-sum-III-437.py │ │ ├── populating │ │ │ ├── populating-next-right-pointers-in-each-node-116.py │ │ │ └── populating-next-right-pointers-in-each-node-II-117.py │ │ ├── serialize-and-deserialize-binary-tree-297.py │ │ ├── sum-root-to-leaf-numbers-129.py │ │ ├── vertical │ │ │ ├── binary-tree-vertical-order-traversal-314.py │ │ │ └── vertical-order-traversal-of-a-binary-tree-987.py │ │ └── view │ │ │ ├── binary-tree-right-side-view-199.py │ │ │ ├── bottom-view.py │ │ │ └── top-view.py │ └── practice │ │ ├── bfs.py │ │ ├── binary-tree-to-graph.py │ │ ├── display.py │ │ ├── insert-in-level-order.py │ │ ├── node-to-root-path.py │ │ ├── print-all-root-to-node-paths.py │ │ ├── print-all-root-to-node-sums.py │ │ ├── root-to-leaf-max-cost-path.py │ │ └── root-to-node-path.py └── bst │ ├── explore.py │ ├── leetcode │ ├── closest-binary-search-tree-value-270.py │ ├── convert-binary-search-tree-to-sorted-doubly-linked-list-426.py │ ├── insert-into-a-binary-search-tree-701.py │ ├── kth-smallest-element-in-a-bst-230.py │ ├── lowest-common-ancestor-of-a-binary-search-tree-235.py │ ├── minimum-absolute-difference-in-bst-530.py │ ├── range │ │ ├── construct-binary-search-tree-from-preorder-traversal-1008.py │ │ ├── serialize-and-deserialize-bst-449.py │ │ └── validate-binary-search-tree-98.py │ ├── search-in-a-binary-search-tree-700.py │ └── unique │ │ ├── unique-binary-search-trees-95.py │ │ └── unique-binary-search-trees-II-96.py │ └── search.py ├── trie ├── leetcode │ ├── hard │ │ ├── design-in-memory-file-system-588-(hashmap).py │ │ ├── design-search-autocomplete-system-642-(hashmap).py │ │ └── word-search-II-212.py │ ├── implement │ │ ├── implement-trie-(hashmap).py │ │ └── implement-trie-(prefix tree)-208-(list).py │ └── search-suggestions-system-1268-(list).py └── print-content │ ├── print-content-(hashmap).py │ └── print-content-(list).py └── warmup └── leetcode ├── grid ├── game-of-life-289.py ├── set-matrix-zeroes-73.py └── spiral-matrix-54.py ├── integer-to-english-words-273.py ├── math ├── number-of-steps-to-reduce-a-number-to-zero-1342.py ├── pow(x,n)-50.py └── reverse-integer-7.py ├── robot-bounded-in-circle-1041.py └── roman ├── integer-to-roman-12.py └── roman-to-integer-13.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *$py.class 4 | 5 | .DS_Store 6 | .idea 7 | /venv 8 | /companies -------------------------------------------------------------------------------- /arrays/digits-of-a-number.py: -------------------------------------------------------------------------------- 1 | def x(n): 2 | res = [] 3 | quot = n 4 | while quot: 5 | res.append(quot % 10) 6 | quot //= 10 7 | res.reverse() 8 | return res 9 | 10 | 11 | def y(n): 12 | res = 0 13 | quot = n 14 | while quot: 15 | res += quot % 10 16 | quot //= 10 17 | return res 18 | 19 | 20 | def z(n): 21 | res = 0 22 | quot = n 23 | while quot: 24 | res = res * 10 + quot % 10 25 | quot //= 10 26 | return res 27 | 28 | 29 | for n in [ 30 | 125, 47, 425, 59, 60, 150, 100 31 | ]: 32 | print(x(n), end=' ') 33 | print(y(n), end=' ') 34 | print(z(n)) 35 | -------------------------------------------------------------------------------- /arrays/leetcode/can-place-flowers-605.py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(nums, n): 3 | if not n: 4 | return True 5 | size = len(nums) 6 | for i in range(size): 7 | if nums[i]: 8 | continue 9 | prev = 0 if not i else nums[i - 1] 10 | next = 0 if i == size - 1 else nums[i + 1] 11 | if not prev and not next: 12 | nums[i] = 1 13 | n -= 1 14 | if not n: 15 | return True 16 | return False 17 | 18 | 19 | for nums, n in [ 20 | ([0, 0, 0, 0, 0, 1, 0, 0], 0), 21 | ([1, 0, 0, 0, 0, 1], 2), 22 | ([1, 0, 0, 0, 1, 0, 0], 2), 23 | ([0, 0, 1, 0, 1], 1), 24 | ([0, 0, 1, 0, 0], 1), 25 | ([0], 1), 26 | ]: 27 | print(main(nums, n)) 28 | -------------------------------------------------------------------------------- /arrays/leetcode/consecutive-numbers-sum-829.py: -------------------------------------------------------------------------------- 1 | def main(n): 2 | def dfs(start, sum, path): 3 | if sum == n: 4 | return 1 5 | if sum < 0: 6 | return 0 7 | res = 0 8 | for i in range(start, n + 1): 9 | if not path or path[-1] + 1 == i: 10 | res += dfs(i + 1, sum + i, path + [i]) 11 | return res 12 | 13 | return dfs(1, 0, []) 14 | 15 | 16 | for n in [ 17 | 4, 5, 7, 8, 9, 15 18 | ]: 19 | print(main(n)) 20 | -------------------------------------------------------------------------------- /arrays/leetcode/grid/minimum-knight-moves-1197.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | def main(dri, dci): 5 | queue = deque([(0, 0, 0)]) 6 | vis = {(0, 0)} 7 | offsets = (-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1) 8 | while queue: 9 | ri, ci, dist = queue.popleft() 10 | if (ri, ci) == (dri, dci): 11 | return dist 12 | for i, j in offsets: 13 | x, y = ri + i, ci + j 14 | if (x, y) not in vis: 15 | vis.add((x, y)) 16 | queue.append((x, y, dist + 1)) 17 | return -1 18 | 19 | 20 | for dri, dci in [ 21 | (2, 1), (5, 5) 22 | ]: 23 | print(main(dri, dci)) 24 | -------------------------------------------------------------------------------- /arrays/leetcode/next-greater-element-III-556.py: -------------------------------------------------------------------------------- 1 | def reverse(nums, i): 2 | low, high = i, len(nums) - 1 3 | while low < high: 4 | nums[low], nums[high] = nums[high], nums[low] 5 | low, high = low + 1, high - 1 6 | 7 | 8 | # T=d,S=d 9 | def main(n): 10 | nums = list(str(n)) 11 | size = len(nums) 12 | i = size - 1 13 | while i >= 1 and nums[i - 1] >= nums[i]: 14 | i -= 1 15 | if not i: 16 | return -1 17 | j = i 18 | while j < size and nums[j] > nums[i - 1]: 19 | j += 1 20 | nums[i - 1], nums[j - 1] = nums[j - 1], nums[i - 1] 21 | reverse(nums, i) 22 | res = int(''.join(nums)) 23 | return res if res < pow(2, 31) else -1 24 | 25 | 26 | for n in [ 27 | 233, 28 | 452654, 29 | 45132, 30 | 321, 31 | 12, 32 | 21, 33 | 2147483486 34 | ]: 35 | print(main(n)) 36 | -------------------------------------------------------------------------------- /arrays/leetcode/prefix-sum/range-addition-370.py: -------------------------------------------------------------------------------- 1 | inputs = [ 2 | (5, [[1, 3, 2], [2, 4, 3], [0, 2, -2]]), 3 | (10, [[2, 4, 6], [5, 6, 8], [1, 9, -4]]) 4 | ] 5 | 6 | 7 | # T=kn,S=1 8 | def main(length, updates): 9 | res = [0] * length 10 | for start, end, val in updates: 11 | for i in range(start, end + 1): 12 | res[i] += val 13 | return res 14 | 15 | 16 | for length, updates in inputs: 17 | print(main(length, updates)) 18 | 19 | print() 20 | 21 | 22 | # T=k+n,S=1 23 | def main(length, updates): 24 | res = [0] * length 25 | for start, end, val in updates: 26 | res[start] += val 27 | if end < length - 1: 28 | res[end + 1] -= val 29 | sum = 0 30 | for i in range(length): 31 | sum += res[i] 32 | res[i] = sum 33 | return res 34 | 35 | 36 | for length, updates in inputs: 37 | print(main(length, updates)) 38 | -------------------------------------------------------------------------------- /arrays/leetcode/single-number-136.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=n 5 | def x(nums): 6 | freq = Counter(nums) 7 | for key, val in freq.items(): 8 | if val == 1: 9 | return key 10 | 11 | 12 | # T=n,S=n 13 | def y(nums): 14 | return 2 * sum(set(nums)) - sum(nums) 15 | 16 | 17 | # T=n,S=1 18 | def z(nums): 19 | res = 0 20 | for val in nums: 21 | res ^= val 22 | return res 23 | 24 | 25 | for nums in [ 26 | [2, 2, 1], 27 | [4, 1, 2, 1, 2] 28 | ]: 29 | print(x(nums), end=' ') 30 | print(y(nums), end=' ') 31 | print(z(nums)) 32 | -------------------------------------------------------------------------------- /arrays/leetcode/two-pointer/2sum/two-sum-II-input-array-is-sorted-167.py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(nums, target): 3 | low, high = 0, len(nums) - 1 4 | while low < high: 5 | sum = nums[low] + nums[high] 6 | if sum == target: 7 | return [low + 1, high + 1] 8 | if sum > target: 9 | high -= 1 10 | else: 11 | low += 1 12 | 13 | 14 | for nums, target in [ 15 | ([2, 7, 11, 15], 9), 16 | ([2, 3, 4], 6), 17 | ([-1, 0], -1) 18 | ]: 19 | print(main(nums, target)) 20 | -------------------------------------------------------------------------------- /arrays/leetcode/two-pointer/2sum/two-sum-less-than-k-1099.py: -------------------------------------------------------------------------------- 1 | # T=nlogn,S=1 2 | def main(nums, k): 3 | n = len(nums) 4 | res = float('-inf') 5 | nums = sorted(nums) 6 | low, high = 0, n - 1 7 | while low < high: 8 | sum = nums[low] + nums[high] 9 | if sum < k: 10 | res = max(res, sum) 11 | low += 1 12 | else: 13 | high -= 1 14 | return res if res != float('-inf') else -1 15 | 16 | 17 | for nums, k in [ 18 | ([34, 23, 1, 24, 75, 33, 54, 8], 60), 19 | ([10, 20, 30], 15) 20 | ]: 21 | print(main(nums, k)) 22 | -------------------------------------------------------------------------------- /arrays/leetcode/two-pointer/3sum/3sum-closest-16.py: -------------------------------------------------------------------------------- 1 | # T=n²,S=1 2 | def main(nums, target): 3 | n = len(nums) 4 | res = float('inf') 5 | nums = sorted(nums) 6 | for i in range(n - 2): 7 | low, high = i + 1, n - 1 8 | while low < high: 9 | sum = nums[i] + nums[low] + nums[high] 10 | if abs(target - sum) < abs(target - res): 11 | res = sum 12 | if sum == target: 13 | return sum 14 | if sum > target: 15 | high -= 1 16 | else: 17 | low += 1 18 | return res 19 | 20 | 21 | for nums, target in [ 22 | ([1, 1, 1, 0], -100), 23 | ([-1, 2, 1, -4], 1), 24 | ([0, 0, 0], 1), 25 | ]: 26 | print(main(nums, target)) 27 | -------------------------------------------------------------------------------- /arrays/leetcode/two-pointer/3sum/3sum-smaller-259.py: -------------------------------------------------------------------------------- 1 | # T=n²,S=1 2 | def main(nums, target): 3 | n = len(nums) 4 | res = 0 5 | nums = sorted(nums) 6 | for i in range(n - 2): 7 | low, high = i + 1, n - 1 8 | while low < high: 9 | sum = nums[i] + nums[low] + nums[high] 10 | if sum < target: 11 | res += high - low 12 | low += 1 13 | else: 14 | high -= 1 15 | return res 16 | 17 | 18 | for nums, target in [ 19 | ([-2, 0, 1, 3], 2), 20 | ([], 0), 21 | ([0], 0) 22 | ]: 23 | print(main(nums, target)) 24 | -------------------------------------------------------------------------------- /arrays/leetcode/two-pointer/remove-duplicates-from-sorted-array-26.py: -------------------------------------------------------------------------------- 1 | def main(nums): 2 | n = len(nums) 3 | 4 | 5 | for nums in [ 6 | [1, 1, 2], 7 | [0, 0, 1, 1, 1, 2, 2, 3, 3, 4] 8 | ]: 9 | print(main(nums)) 10 | -------------------------------------------------------------------------------- /arrays/leetcode/two-pointer/remove-duplicates-from-sorted-array-II-80.py: -------------------------------------------------------------------------------- 1 | def main(nums): 2 | n = len(nums) 3 | 4 | 5 | for nums in [ 6 | [1, 1, 1, 2, 2, 3], 7 | [0, 0, 1, 1, 1, 1, 2, 3, 3] 8 | ]: 9 | print(main(nums)) 10 | -------------------------------------------------------------------------------- /arrays/leetcode/two-pointer/rotate-array-189.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(nums, k): 3 | n = len(nums) 4 | res = [0] * n 5 | for i in range(n): 6 | res[(i + k) % n] = nums[i] 7 | nums[:] = res 8 | 9 | 10 | for nums, k in [ 11 | ([1, 2, 3, 4, 5, 6, 7], 3), 12 | ([-1, -100, 3, 99], 2) 13 | ]: 14 | main(nums, k) 15 | print(nums) 16 | 17 | 18 | def reverse(nums, low, high): 19 | while low < high: 20 | nums[low], nums[high] = nums[high], nums[low] 21 | low, high = low + 1, high - 1 22 | 23 | 24 | # T=n,S=1 25 | def main(nums, k): 26 | n = len(nums) 27 | k %= n 28 | reverse(nums, 0, n - k - 1) 29 | reverse(nums, n - k, n - 1) 30 | reverse(nums, 0, n - 1) 31 | 32 | 33 | for nums, k in [ 34 | ([1, 2, 3, 4, 5, 6, 7], 3), 35 | ([8, 4, 1, 3, 9, 6], 3), 36 | ([-1, -100, 3, 99], 2) 37 | ]: 38 | main(nums, k) 39 | print(nums) 40 | -------------------------------------------------------------------------------- /arrays/practice.py: -------------------------------------------------------------------------------- 1 | nums = 14 2 | b = 21 3 | 4 | print(nums ^ b) 5 | print(nums ^ nums) 6 | print(b ^ b) 7 | 8 | x = (4, 3) 9 | 10 | nums, b = x 11 | print(nums, b) 12 | 13 | nums = [5, 5, 4, 5, 4, 1, 1, 1] 14 | print(nums) -------------------------------------------------------------------------------- /arrays/rotate-matrix/rotate-matrix-90-clockwise.py: -------------------------------------------------------------------------------- 1 | # T=n²,S=1 2 | def main(grid): 3 | n = len(grid) 4 | for i in range(n): 5 | for j in range(i + 1, n): 6 | grid[i][j], grid[j][i] = grid[j][i], grid[i][j] 7 | for i in range(n): 8 | low, high = 0, n - 1 9 | while low < high: 10 | grid[i][low], grid[i][high] = grid[i][high], grid[i][low] 11 | low, high = low + 1, high - 1 12 | 13 | 14 | for grid in [ 15 | [ 16 | [1, 2, 3, 4], 17 | [5, 6, 7, 8], 18 | [9, 10, 11, 12], 19 | [13, 14, 15, 16] 20 | ] 21 | ]: 22 | main(grid) 23 | print(grid) 24 | -------------------------------------------------------------------------------- /arrays/two-sorted-arrays/merge.py: -------------------------------------------------------------------------------- 1 | # T=m+n,S=m+n 2 | def main(nums1, nums2): 3 | m, n = len(nums1), len(nums2) 4 | res = [] 5 | i = j = 0 6 | while i < m and j < n: 7 | if nums1[i] < nums2[j]: 8 | res.append(nums1[i]) 9 | i += 1 10 | else: 11 | res.append(nums2[j]) 12 | j += 1 13 | while i < m: 14 | res.append(nums1[i]) 15 | i += 1 16 | while j < n: 17 | res.append(nums2[j]) 18 | j += 1 19 | return res 20 | 21 | 22 | for nums1, nums2 in [ 23 | ([2, 5, 12, 18, 20], [7, 9, 11, 15, 25, 28, 30, 35]), 24 | ([1, 3, 5, 7], [2, 4, 6, 8]), 25 | ([1, 2, 3], [2, 5, 6]), 26 | ([4, 5, 6], [1, 2, 3]) 27 | ]: 28 | print(main(nums1, nums2)) 29 | -------------------------------------------------------------------------------- /binary-search/leetcode/binary-search-704.py: -------------------------------------------------------------------------------- 1 | # T=logn,S=1 2 | def main(nums, target): 3 | low, high = 0, len(nums) - 1 4 | while low <= high: 5 | mid = low + (high - low) // 2 6 | if target == nums[mid]: 7 | return mid 8 | elif target > nums[mid]: 9 | low = mid + 1 10 | else: 11 | high = mid - 1 12 | return -1 13 | 14 | 15 | for nums, target in [ 16 | ([-1, 0, 3, 5, 9, 12], 9), 17 | ([-1, 0, 3, 5, 9, 12], 2), 18 | ([4, 5], 2), 19 | ([4, 5], 8), 20 | ([5], 2), 21 | ([5], 5), 22 | ]: 23 | print(main(nums, target)) 24 | -------------------------------------------------------------------------------- /binary-search/leetcode/first-and-last/count-elements<=target-in-sorted-array.py: -------------------------------------------------------------------------------- 1 | def main(nums, target): 2 | low, high = 0, len(nums) - 1 3 | while low <= high: 4 | mid = low + (high - low) // 2 5 | if target >= nums[mid]: 6 | low = mid + 1 7 | else: 8 | high = mid - 1 9 | return low 10 | 11 | 12 | for (nums, target) in [ 13 | ([1, 4, 7, 8, 8, 8, 8, 10, 12, 14, 16], 8), 14 | ([1, 2, 3, 5, 8, 12], 5), 15 | ([1, 2, 3, 5, 8, 12], 4), 16 | ([1, 2, 3, 5, 8, 12], 8), 17 | ([1, 2, 3, 5, 8, 12], 15), 18 | ([1, 2, 3, 5, 8, 12], -5) 19 | ]: 20 | print(main(nums, target), end=' ') 21 | -------------------------------------------------------------------------------- /binary-search/leetcode/first-and-last/count-elements>=target-in-sorted-array.py: -------------------------------------------------------------------------------- 1 | def main(nums, target): 2 | low, high = 0, len(nums) - 1 3 | while low <= high: 4 | mid = low + (high - low) // 2 5 | if target <= nums[mid]: 6 | high = mid - 1 7 | else: 8 | low = mid + 1 9 | return len(nums) - high - 1 10 | 11 | 12 | for (nums, target) in [ 13 | ([1, 4, 7, 8, 8, 8, 8, 10, 12, 14, 16], 8), 14 | ([1, 2, 3, 5, 8, 12], 5), 15 | ([1, 2, 3, 5, 8, 12], 4), 16 | ([1, 2, 3, 5, 8, 12], 8), 17 | ([1, 2, 3, 5, 8, 12], 15), 18 | ([1, 2, 3, 5, 8, 12], -5) 19 | ]: 20 | print(main(nums, target), end=' ') 21 | -------------------------------------------------------------------------------- /binary-search/leetcode/first-and-last/first-bad-version-278.py: -------------------------------------------------------------------------------- 1 | # T=n 2 | def x(n): 3 | for version in range(1, n + 1): 4 | if isBadVersion(version): 5 | return version 6 | 7 | 8 | # T=logn 9 | def y(n): 10 | res = low = 1 11 | high = n 12 | while low <= high: 13 | mid = low + (high - low) // 2 14 | if isBadVersion(mid): 15 | res = mid 16 | high = mid - 1 17 | else: 18 | low = mid + 1 19 | return res 20 | -------------------------------------------------------------------------------- /binary-search/leetcode/first-and-last/first-strictly-greater-element.py: -------------------------------------------------------------------------------- 1 | def main(nums, target): 2 | low, high = 0, len(nums) - 1 3 | while low <= high: 4 | mid = low + (high - low) // 2 5 | if target >= nums[mid]: 6 | low = mid + 1 7 | else: 8 | high = mid - 1 9 | return None if low == len(nums) else nums[low] 10 | 11 | 12 | for (nums, target) in [ 13 | ([1, 4, 7, 8, 8, 8, 8, 10, 12, 14, 16], 8), 14 | ([1, 2, 3, 5, 8, 12], 5), 15 | ([1, 2, 3, 5, 8, 12], 4), 16 | ([1, 2, 3, 5, 8, 12], 8), 17 | ([1, 2, 3, 5, 8, 12], 15), 18 | ([1, 2, 3, 5, 8, 12], -5) 19 | ]: 20 | print(main(nums, target)) 21 | -------------------------------------------------------------------------------- /binary-search/leetcode/first-and-last/first-strictly-smaller-element.py: -------------------------------------------------------------------------------- 1 | def main(nums, target): 2 | low, high = 0, len(nums) - 1 3 | while low <= high: 4 | mid = low + (high - low) // 2 5 | if target <= nums[mid]: 6 | high = mid - 1 7 | else: 8 | low = mid + 1 9 | return None if high == -1 else nums[high] 10 | 11 | 12 | for (nums, target) in [ 13 | ([1, 4, 7, 8, 8, 8, 8, 10, 12, 14, 16], 8), 14 | ([1, 2, 3, 5, 8, 12], 5), 15 | ([1, 2, 3, 5, 8, 12], 4), 16 | ([1, 2, 3, 5, 8, 12], 8), 17 | ([1, 2, 3, 5, 8, 12], 15), 18 | ([1, 2, 3, 5, 8, 12], -5) 19 | ]: 20 | print(main(nums, target)) 21 | -------------------------------------------------------------------------------- /binary-search/leetcode/math/sqrt(x)-69.py: -------------------------------------------------------------------------------- 1 | # T=logn,S=1 2 | def main(target): 3 | low, high = 0, target 4 | while low <= high: 5 | mid = low + (high - low) // 2 6 | if mid * mid <= target < (mid + 1) * (mid + 1): 7 | return mid 8 | elif target > mid * mid: 9 | low = mid + 1 10 | else: 11 | high = mid - 1 12 | 13 | 14 | for target in [ 15 | 0, 1, 4, 8, 9, 10, 2 ** 31 - 1 16 | ]: 17 | print(main(target), end=' ') 18 | -------------------------------------------------------------------------------- /binary-search/leetcode/math/valid-perfect-square-367.py: -------------------------------------------------------------------------------- 1 | def main(n): 2 | if n == 1: 3 | return True 4 | low, high = 1, n // 2 5 | while low <= high: 6 | mid = low + (high - low) // 2 7 | val = mid * mid 8 | if val == n: 9 | return True 10 | elif val > n: 11 | high = mid - 1 12 | else: 13 | low = mid + 1 14 | return False 15 | 16 | 17 | for n in [1, 14, 16]: 18 | print(main(n)) 19 | -------------------------------------------------------------------------------- /binary-search/leetcode/peak/find-peak-element-162.py: -------------------------------------------------------------------------------- 1 | # T=logn,S=1 2 | def main(nums): 3 | low, high = 0, len(nums) - 1 4 | while low < high: 5 | mid = low + (high - low) // 2 6 | if nums[mid + 1] > nums[mid]: 7 | low = mid + 1 8 | elif nums[mid - 1] > nums[mid]: 9 | high = mid 10 | else: 11 | return mid 12 | return low 13 | 14 | 15 | for nums in [ 16 | [1, 2, 3, 1], 17 | [1, 2, 1, 3, 5, 6, 4], 18 | [1], 19 | [1, 2], 20 | [2, 1], 21 | [1, 2, 1], 22 | [1, 2, 3], 23 | [1, 2, 3, 4, 7, 3, 2, 1, 0] 24 | ]: 25 | print(str(nums) + '->' + str(main(nums))) 26 | -------------------------------------------------------------------------------- /binary-search/leetcode/peak/peak-index-in-a-mountain-array-852.py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def x(nums): 3 | for i in range(1, len(nums) - 1): 4 | if nums[i - 1] < nums[i] > nums[i + 1]: 5 | return i 6 | return -1 7 | 8 | 9 | # T=logn,S=1 10 | def y(nums): 11 | low, high = 0, len(nums) - 1 12 | while low < high: 13 | mid = low + (high - low) // 2 14 | if nums[mid + 1] > nums[mid]: 15 | low = mid + 1 16 | elif nums[mid - 1] > nums[mid]: 17 | high = mid 18 | else: 19 | return mid 20 | return -1 21 | 22 | 23 | for nums in [ 24 | [1, 2, 3, 4], 25 | [0, 1, 0], 26 | [0, 2, 1, 0], 27 | [0, 10, 5, 2], 28 | [3, 4, 5, 1], 29 | [24, 69, 100, 99, 79, 78, 67, 36, 26, 19], 30 | ]: 31 | print(x(nums), end=' ') 32 | print(y(nums)) 33 | -------------------------------------------------------------------------------- /binary-search/leetcode/range/capacity-to-ship-packages-within-d-days-1011.py: -------------------------------------------------------------------------------- 1 | # T=nlog(sum),S=1 2 | def main(nums, m): 3 | def count(mid): 4 | sum, count = 0, 1 5 | for i in range(len(nums)): 6 | sum += nums[i] 7 | if sum > mid: 8 | sum, count = nums[i], count + 1 9 | return count 10 | 11 | low, high = max(nums), sum(nums) 12 | while low <= high: 13 | mid = low + (high - low) // 2 14 | if count(mid) <= m: 15 | high = mid - 1 16 | else: 17 | low = mid + 1 18 | return low 19 | 20 | 21 | for nums, m in [ 22 | ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5), 23 | ([3, 2, 2, 4, 1, 4], 3), 24 | ([1, 2, 3, 1, 1], 4) 25 | ]: 26 | print(main(nums, m), end=' ') 27 | -------------------------------------------------------------------------------- /binary-search/leetcode/rotated-sorted-array/find-minimum-in-rotated-sorted-array-153.py: -------------------------------------------------------------------------------- 1 | # T=logn,S=1 2 | def main(nums): 3 | low, high = 0, len(nums) - 1 4 | while low < high: 5 | mid = low + (high - low) // 2 6 | if nums[mid] < nums[high]: 7 | high = mid 8 | elif nums[mid] > nums[high]: 9 | low = mid + 1 10 | return nums[low] 11 | 12 | 13 | for nums in [ 14 | [3, 4, 5, 1, 2], 15 | [4, 5, 6, 7, 0, 1, 2], 16 | [11, 13, 15, 17], 17 | [1] 18 | ]: 19 | print(main(nums)) 20 | -------------------------------------------------------------------------------- /binary-search/leetcode/rotated-sorted-array/find-minimum-in-rotated-sorted-array-II-154-(duplicates).py: -------------------------------------------------------------------------------- 1 | # T=logn,S=1 2 | def main(nums): 3 | low, high = 0, len(nums) - 1 4 | while low < high: 5 | mid = low + (high - low) // 2 6 | if nums[mid] < nums[high]: 7 | high = mid 8 | elif nums[mid] > nums[high]: 9 | low = mid + 1 10 | else: 11 | if nums[high - 1] > nums[high]: 12 | low = high 13 | break 14 | high -= 1 15 | return nums[low] 16 | 17 | 18 | for nums in [ 19 | [1, 1, 1, 1, 1, 1, 2], 20 | [1, 1, 1, 1, 1, 1, 2, 1], 21 | [1, 3, 5], 22 | [2, 2, 2, 0, 1], 23 | [1, 1], 24 | [1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1], 25 | [1, 1, 1, 1, 2, 1, 1] 26 | ]: 27 | print(main(nums)) 28 | -------------------------------------------------------------------------------- /binary-search/leetcode/rotated-sorted-array/pivot-index.py: -------------------------------------------------------------------------------- 1 | # T=logn,S=1 2 | def main(nums): 3 | low, high = 0, len(nums) - 1 4 | while low < high: 5 | mid = low + (high - low) // 2 6 | if nums[mid] < nums[high]: 7 | high = mid 8 | elif nums[mid] > nums[high]: 9 | low = mid + 1 10 | return low 11 | 12 | 13 | for nums in [ 14 | [1], 15 | [1, 2], 16 | [2, 1], 17 | [3, 1], 18 | [1, 2, 3], 19 | [3, 1, 2], 20 | [2, 3, 1], 21 | [1, 2, 3, 4], 22 | [4, 1, 2, 3], 23 | [3, 4, 1, 2], 24 | [2, 3, 4, 1], 25 | [1, 2, 3, 4, 5], 26 | [5, 1, 2, 3, 4], 27 | [4, 5, 1, 2, 3], 28 | [3, 4, 5, 1, 2], 29 | [2, 3, 4, 5, 1], 30 | [5, 6, 7, 8, 9, 10, 1, 2, 3, 4], 31 | [10, 1, 2, 3, 4, 5, 6, 7, 8, 9] 32 | ]: 33 | print(str(nums) + ' -> ' + str(main(nums))) 34 | -------------------------------------------------------------------------------- /binary-search/leetcode/search-in-a-sorted-array-of-unknown-size-702.py: -------------------------------------------------------------------------------- 1 | class ArrayReader: 2 | 3 | def __init__(self, arr): 4 | self.arr = arr 5 | 6 | def get(self, i): 7 | return self.arr[i] 8 | 9 | 10 | # T=logT,S=1 11 | def main(reader, target): 12 | low, high = 0, 1 13 | while reader.get(high) < target: 14 | low, high = high, 2 * high 15 | while low <= high: 16 | mid = low + (high - low) // 2 17 | val = reader.get(mid) 18 | if target == val: 19 | return mid 20 | elif target > val: 21 | low = mid + 1 22 | else: 23 | high = mid - 1 24 | return -1 25 | 26 | 27 | for nums, target in [ 28 | ([-1, 0, 3, 5, 9, 12], 9), 29 | ([-1, 0, 3, 5, 9, 12], 2), 30 | ]: 31 | print(main(ArrayReader(nums), target)) 32 | -------------------------------------------------------------------------------- /binary-search/leetcode/search-insert-position/find-smallest-letter-greater-than-target-744.py: -------------------------------------------------------------------------------- 1 | # T=logn,S=1 2 | def main(nums, target): 3 | n = len(nums) 4 | low, high = 0, n - 1 5 | while low <= high: 6 | mid = low + (high - low) // 2 7 | if target >= nums[mid]: 8 | low = mid + 1 9 | else: 10 | high = mid - 1 11 | return nums[low % n] 12 | 13 | 14 | for nums, target in [ 15 | (['c', 'f', 'j'], 'a'), 16 | (['c', 'f', 'j'], 'c'), 17 | (['c', 'f', 'j'], 'd'), 18 | (['c', 'f', 'j'], 'g'), 19 | (['c', 'f', 'j'], 'j'), 20 | (['c', 'f', 'j'], 'k'), 21 | (['e', 'e', 'e', 'e', 'e', 'e', 'n', 'n', 'n', 'n'], 'e') 22 | ]: 23 | print(main(nums, target), end=' ') 24 | -------------------------------------------------------------------------------- /binary-search/min-max-key.py: -------------------------------------------------------------------------------- 1 | print(min('c', 'b', 'a', 'Y', 'Z')) 2 | print(min('c', 'b', 'a', 'Y', 'Z', key=str.lower)) 3 | print(min('java', 'python', 'z++')) 4 | print(min('java', 'python', 'z++', key=len)) 5 | -------------------------------------------------------------------------------- /binary-search/minimum-difference-pair.py: -------------------------------------------------------------------------------- 1 | # T=n² 2 | def x(nums): 3 | n = len(nums) 4 | least = float('inf') 5 | for i in range(n - 1): 6 | for j in range(i + 1, n): 7 | least = min(least, abs(nums[j] - nums[i])) 8 | return least 9 | 10 | 11 | # T=nlogn 12 | def y(nums): 13 | nums.sort() 14 | n = len(nums) 15 | return min(abs(nums[i + 1] - nums[i]) for i in range(n - 1)) 16 | 17 | 18 | for nums in [ 19 | [1, 5, 3, 19, 18, 25], 20 | [30, 5, 20, 9], 21 | [1, 19, -4, 31, 38, 25, 100] 22 | ]: 23 | print(x(nums), end=' ') 24 | print(y(nums)) 25 | -------------------------------------------------------------------------------- /binary-search/smallest-missing-number-in-sorted-array.py: -------------------------------------------------------------------------------- 1 | # T=n 2 | def x(nums): 3 | n = len(nums) 4 | for i in range(n): 5 | if i != nums[i]: 6 | return i 7 | return n 8 | 9 | 10 | # T=logn 11 | def y(nums): 12 | low, high = 0, len(nums) - 1 13 | while low <= high: 14 | mid = low + (high - low) // 2 15 | if nums[mid] != mid: 16 | high = mid - 1 17 | else: 18 | low = mid + 1 19 | return low 20 | 21 | 22 | for nums in [ 23 | [0, 1, 2, 6, 9, 11, 15], 24 | [1, 2, 3, 4, 6, 9, 11, 15], 25 | [0, 1, 2, 3, 4, 5, 6] 26 | ]: 27 | print(x(nums), end=' ') 28 | print(y(nums)) 29 | -------------------------------------------------------------------------------- /companies/leetcode.py: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/minimum-deletion-cost-to-avoid-repeating-letters/ 2 | # https://leetcode.com/problems/minimum-deletions-to-make-character-frequencies-unique/ 3 | 4 | # Swiggy 5 | 6 | # https://leetcode.com/problems/minimum-one-bit-operations-to-make-integers-zero/ 7 | # https://leetcode.com/problems/maximum-area-of-a-piece-of-cake-after-horizontal-and-vertical-cuts/ 8 | -------------------------------------------------------------------------------- /companies/links.py: -------------------------------------------------------------------------------- 1 | # https://www.geeksforgeeks.org/minimum-characters-required-to-be-removed-to-make-frequency-of-each-character-unique/ 2 | 3 | # Swiggy 4 | 5 | # https://snippets.cacher.io/snippet/e08f0fec1e61bf8f22a6 6 | # https://www.codingninjas.com/codestudio/problem-details/break-the-prison_1755915 7 | # https://leetcode.com/discuss/interview-question/1002082/twillio-oa-prison-break 8 | # https://www.geeksforgeeks.org/minimum-number-of-given-operations-required-to-be-performed-to-reduce-n-to-0/ 9 | -------------------------------------------------------------------------------- /companies/solve-1.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | url = 'https://jsonmock.hackerrank.com/api/movies/search/?Title=' 4 | 5 | 6 | def main(substr): 7 | res, totalPages = [], requests.get(url + substr).json()['total_pages'] 8 | for pageNumber in range(1, totalPages + 1): 9 | for response in requests.get(url + substr + '&page=' + str(pageNumber)).json()['data']: 10 | res.append(response['Title']) 11 | res.sort() 12 | return res 13 | 14 | 15 | for substr in [ 16 | 'spiderman' 17 | ]: 18 | print(main(substr)) 19 | -------------------------------------------------------------------------------- /companies/solve-2.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # https://www.geeksforgeeks.org/removing-string-that-is-an-anagram-of-an-earlier-string/ 5 | def main(text): 6 | res, vis = [], set() 7 | for i in range(len(text)): 8 | word = ''.join(sorted(text[i])) 9 | if word not in vis: 10 | res.append(text[i]) 11 | vis.add(word) 12 | res.sort() 13 | return res 14 | 15 | 16 | for text in [ 17 | ['code', 'doce', 'ecod', 'framer', 'frame'], 18 | ['code', 'aaagmnrs', 'anagrams', 'doce'], 19 | ['poke', 'pkoe', 'okpe', 'ekop'] 20 | ]: 21 | print(main(text)) 22 | 23 | # https://github.com/AnoshaRehan/Coding-Challenges/blob/main/damDesign.py 24 | -------------------------------------------------------------------------------- /cyclic-sort/cyclic-sort.py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(nums): 3 | n = len(nums) 4 | presentIndex = 0 5 | while presentIndex < n: 6 | correctIndex = nums[presentIndex] - 1 7 | if nums[presentIndex] != nums[correctIndex]: 8 | nums[correctIndex], nums[presentIndex] = nums[presentIndex], nums[correctIndex] 9 | else: 10 | presentIndex += 1 11 | 12 | 13 | for nums in [ 14 | [], 15 | [1], 16 | [2, 1], 17 | [5, 4, 3, 2, 1], 18 | [3, 4, 6, 2, 1, 5], 19 | [4, 1, 3, 5, 6, 2], 20 | [2, 6, 4, 1, 3, 5], 21 | [8, 6, 4, 2, 3, 5, 7, 1] 22 | ]: 23 | main(nums) 24 | print(nums) 25 | -------------------------------------------------------------------------------- /cyclic-sort/leetcode/duplicate/find-all-duplicates-in-an-array-442.py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(nums): 3 | res = [] 4 | n = len(nums) 5 | presentIndex = 0 6 | while presentIndex < n: 7 | correctIndex = nums[presentIndex] - 1 8 | if -1 < correctIndex < n and nums[presentIndex] != nums[correctIndex]: 9 | nums[correctIndex], nums[presentIndex] = nums[presentIndex], nums[correctIndex] 10 | else: 11 | presentIndex += 1 12 | for presentIndex in range(n): 13 | if presentIndex != nums[presentIndex] - 1: 14 | res.append(nums[presentIndex]) 15 | return res 16 | 17 | 18 | for nums in [ 19 | [2, 6, 4, 4, 3, 2], 20 | [3, 4, 4, 5, 5], 21 | [5, 4, 7, 2, 3, 5, 3], 22 | [2, 3, 1, 8, 2, 3, 5, 1], 23 | [2, 4, 1, 2], 24 | [2, 3, 1, 2], 25 | [4, 3, 2, 7, 8, 2, 3, 1], 26 | [1, 1, 2], 27 | [1] 28 | ]: 29 | print(main(nums)) 30 | -------------------------------------------------------------------------------- /cyclic-sort/leetcode/set-mismatch-645.py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(nums): 3 | n = len(nums) 4 | presentIndex = 0 5 | while presentIndex < n: 6 | correctIndex = nums[presentIndex] - 1 7 | if -1 < correctIndex < n and nums[presentIndex] != nums[correctIndex]: 8 | nums[correctIndex], nums[presentIndex] = nums[presentIndex], nums[correctIndex] 9 | else: 10 | presentIndex += 1 11 | for presentIndex in range(n): 12 | if presentIndex != nums[presentIndex] - 1: 13 | return [nums[presentIndex], presentIndex + 1] 14 | 15 | 16 | for nums in [ 17 | [4, 3, 4, 5, 1], 18 | [4, 3, 5, 4, 1], 19 | [1, 2, 2, 4], 20 | [1, 1] 21 | ]: 22 | print(main(nums)) 23 | -------------------------------------------------------------------------------- /cyclic-sort/missing-number.py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(nums): 3 | n = len(nums) 4 | presentIndex = 0 5 | while presentIndex < n: 6 | correctIndex = nums[presentIndex] - 1 7 | if -1 < correctIndex < n and nums[presentIndex] != nums[correctIndex]: 8 | nums[correctIndex], nums[presentIndex] = nums[presentIndex], nums[correctIndex] 9 | else: 10 | presentIndex += 1 11 | for presentIndex in range(n): 12 | if presentIndex != nums[presentIndex] - 1: 13 | return presentIndex + 1 14 | return None 15 | 16 | 17 | for nums in [ 18 | [3, 2, 4, 42, 1], 19 | [3, 2, 4, 5, 78], 20 | [1, 2, 4, 3], 21 | [2, 1, 3] 22 | ]: 23 | print(main(nums), nums) 24 | -------------------------------------------------------------------------------- /design/explore.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | print(random.randint(1, 5)) 4 | random.choice([1, 2, 3, 4, 5]) 5 | print(random.choice(list({1, 2, 3}))) 6 | 7 | a = [8, 12, 17, 91, 24] 8 | 9 | print(any([1, 3, 4])) 10 | 11 | nums = [7, 2, 5, 9] 12 | hm = { 13 | 7: 0, 14 | 2: 1, 15 | 5: 2, 16 | 9: 3 17 | } 18 | 19 | print(nums) 20 | print(hm) 21 | 22 | val = 2 23 | last = nums[-1] 24 | index = hm[val] 25 | nums[index] = last 26 | hm[last] = index 27 | nums.pop() 28 | del hm[val] 29 | 30 | print(nums) 31 | print(hm) 32 | -------------------------------------------------------------------------------- /design/leetcode/LFU-cache-460.py: -------------------------------------------------------------------------------- 1 | class LFUCache: 2 | 3 | def __init__(self, capacity): 4 | self.capacity = capacity 5 | 6 | def get(self, key): 7 | pass 8 | 9 | def put(self, key, value): 10 | pass 11 | -------------------------------------------------------------------------------- /dynamic-programming/digit-dynamic-programming/atcoder/max-sum-of-digits-in-numbers-till-n-(digit-sum-2).py: -------------------------------------------------------------------------------- 1 | def total(n): 2 | return sum(int(digit) for digit in n) 3 | 4 | 5 | def x(n): 6 | n = str(n) 7 | size = len(n) 8 | return max(total(str(int(n[0]) - 1) + (size - 1) * '9'), total(n)) 9 | 10 | 11 | def y(n): 12 | n, cache = str(n), {} 13 | size = len(n) 14 | 15 | def dfs(i, restrict, sum): 16 | if i == size: 17 | return sum 18 | key = i, restrict, sum 19 | if key in cache: 20 | return cache[key] 21 | cache[key], limit = 0, int(n[i]) if restrict else 9 22 | for digit in range(limit + 1): 23 | cache[key] = max(cache[key], dfs(i + 1, False if digit < limit else restrict, sum + digit)) 24 | return cache[key] 25 | 26 | return dfs(0, True, 0) 27 | 28 | 29 | for n in [100, 9995, 3141592653589793, 2999]: 30 | print(x(n), end=' ') 31 | print(y(n)) 32 | -------------------------------------------------------------------------------- /dynamic-programming/digit-dynamic-programming/leetcode/number-of-digit-one-233.py: -------------------------------------------------------------------------------- 1 | # T=logn,S=logn 2 | def x(n): 3 | n, cache = str(n), {} 4 | size = len(n) 5 | 6 | def dfs(i, restrict, oneCount): 7 | if i == size: 8 | return oneCount 9 | key = i, restrict, oneCount 10 | if key in cache: 11 | return cache[key] 12 | cache[key], limit = 0, int(n[i]) if restrict else 9 13 | for digit in range(limit + 1): 14 | cache[key] += dfs(i + 1, False if digit < limit else restrict, oneCount + (digit is 1)) 15 | return cache[key] 16 | 17 | return dfs(0, True, 0) 18 | 19 | 20 | for n in [ 21 | 13, 0 22 | ]: 23 | print(x(n)) 24 | -------------------------------------------------------------------------------- /dynamic-programming/digit-dynamic-programming/practice/count-n-digit-numbers.py: -------------------------------------------------------------------------------- 1 | def main(n): 2 | cache = {n: 1} 3 | 4 | def dfs(i): 5 | if i in cache: 6 | return cache[i] 7 | cache[i] = 0 8 | for digit in range(10): 9 | if not i and not digit: 10 | continue 11 | cache[i] += dfs(i + 1) 12 | return cache[i] 13 | 14 | return dfs(0) 15 | 16 | 17 | for d in [1, 2, 3]: 18 | print(main(d)) 19 | -------------------------------------------------------------------------------- /dynamic-programming/digit-dynamic-programming/practice/get-n-digit-numbers.py: -------------------------------------------------------------------------------- 1 | def x(n): 2 | def dfs(i, num): 3 | if i == n: 4 | return [num] 5 | res = [] 6 | for digit in range(10): 7 | if not i and not digit: 8 | continue 9 | res += dfs(i + 1, num * 10 + digit) 10 | return res 11 | 12 | return dfs(0, 0) 13 | 14 | 15 | def y(n): 16 | def dfs(i): 17 | if i == n: 18 | return [0] 19 | res = [] 20 | for digit in range(10): 21 | if not i and not digit: 22 | continue 23 | for num in dfs(i + 1): 24 | res.append(digit * pow(10, n - 1 - i) + num) 25 | return res 26 | 27 | return dfs(0) 28 | 29 | 30 | for n in [2]: 31 | print(x(n)) 32 | print(y(n)) 33 | -------------------------------------------------------------------------------- /dynamic-programming/digit-dynamic-programming/spoj/sum-of-digits-of-numbers-in-range-(digit-sum).py: -------------------------------------------------------------------------------- 1 | def x(nums, b): 2 | def helper(n): 3 | n, cache = str(n), {} 4 | size = len(n) 5 | 6 | def dfs(i, restrict, sum): 7 | if i == size: 8 | return sum 9 | key = i, restrict, sum 10 | if key in cache: 11 | return cache[key] 12 | cache[key] = 0 13 | limit = int(n[i]) if restrict else 9 14 | for digit in range(limit + 1): 15 | cache[key] += dfs(i + 1, False if digit < limit else restrict, sum + digit) 16 | return cache[key] 17 | 18 | return dfs(0, True, 0) 19 | 20 | return helper(high) - helper(low - 1) if low else helper(high) 21 | 22 | 23 | for low, high in [ 24 | (0, 10), 25 | (28, 31), 26 | (1234, 56789) 27 | ]: 28 | print(x(low, high)) 29 | -------------------------------------------------------------------------------- /dynamic-programming/explore.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikhilgoyal104/dsa/78b655dd8a9ba1b064ed0589cf6c35052c0560c6/dynamic-programming/explore.py -------------------------------------------------------------------------------- /dynamic-programming/first-and-second-minimum.py: -------------------------------------------------------------------------------- 1 | nums = [1, 2, 1, -5, 3, 4, -5, -1, -2] 2 | firstMin = secondMin = float('inf') 3 | 4 | for val in nums: 5 | if val < firstMin: 6 | secondMin = firstMin 7 | firstMin = val 8 | elif val < secondMin: 9 | secondMin = val 10 | 11 | print(firstMin, secondMin) 12 | 13 | nums = [1, 2, 1, -5, 3, 4, -5, -1, -2] 14 | firstMin = secondMin = float('inf') 15 | 16 | for val in nums: 17 | if val < firstMin: 18 | secondMin = firstMin 19 | firstMin = val 20 | elif firstMin < val < secondMin: 21 | secondMin = val 22 | 23 | print(firstMin, secondMin) 24 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/buy-and-sell-stock/best-time-to-buy-and-sell-stock-II-122-(∞ transactions).py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(nums): 3 | n = len(nums) 4 | cache = {} 5 | 6 | def dfs(i, bought): 7 | if i == n: 8 | return 0 9 | key = i, bought 10 | if key in cache: 11 | return cache[key] 12 | nothing = dfs(i + 1, bought) 13 | if bought: 14 | sell = dfs(i + 1, False) + nums[i] 15 | cache[key] = max(sell, nothing) 16 | return cache[key] 17 | buy = dfs(i + 1, True) - nums[i] 18 | cache[key] = max(buy, nothing) 19 | return cache[key] 20 | 21 | return dfs(0, False) 22 | 23 | 24 | for nums in [ 25 | [7, 1, 5, 3, 6, 4], 26 | [1, 2, 3, 4, 5], 27 | [7, 6, 4, 3, 1] 28 | ]: 29 | print(main(nums)) 30 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/buy-and-sell-stock/best-time-to-buy-and-sell-stock-III-123-(2 transactions).py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(nums): 3 | n = len(nums) 4 | cache = {} 5 | 6 | def dfs(i, bought, k): 7 | if i == n or not k: 8 | return 0 9 | key = i, bought, k 10 | if key in cache: 11 | return cache[key] 12 | nothing = dfs(i + 1, bought, k) 13 | if bought: 14 | sell = dfs(i + 1, False, k - 1) + nums[i] 15 | cache[key] = max(sell, nothing) 16 | return cache[key] 17 | buy = dfs(i + 1, True, k) - nums[i] 18 | cache[key] = max(buy, nothing) 19 | return cache[key] 20 | 21 | return dfs(0, False, 2) 22 | 23 | 24 | for nums in [ 25 | [3, 3, 5, 0, 0, 3, 1, 4], 26 | [1, 2, 3, 4, 5], 27 | [7, 6, 4, 3, 1], 28 | [1] 29 | ]: 30 | print(main(nums)) 31 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/buy-and-sell-stock/best-time-to-buy-and-sell-stock-IV-188-(k-transactions).py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(k, nums): 3 | n = len(nums) 4 | cache = {} 5 | 6 | def dfs(i, bought, k): 7 | if i == n or not k: 8 | return 0 9 | key = i, bought, k 10 | if key in cache: 11 | return cache[key] 12 | nothing = dfs(i + 1, bought, k) 13 | if bought: 14 | sell = dfs(i + 1, False, k - 1) + nums[i] 15 | cache[key] = max(sell, nothing) 16 | return cache[key] 17 | buy = dfs(i + 1, True, k) - nums[i] 18 | cache[key] = max(buy, nothing) 19 | return cache[key] 20 | 21 | return dfs(0, False, k) 22 | 23 | 24 | for k, nums in [ 25 | (2, [2, 4, 1]), 26 | (2, [3, 2, 6, 5, 0, 3]) 27 | ]: 28 | print((main(k, nums))) 29 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/buy-and-sell-stock/best-time-to-buy-and-sell-stock-with-cooldown-309-(∞ transactions).py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(nums): 3 | n = len(nums) 4 | cache = {} 5 | 6 | def dfs(i, bought): 7 | if i >= n: 8 | return 0 9 | key = i, bought 10 | if key in cache: 11 | return cache[key] 12 | nothing = dfs(i + 1, bought) 13 | if bought: 14 | sell = dfs(i + 2, False) + nums[i] 15 | cache[key] = max(sell, nothing) 16 | return cache[key] 17 | buy = dfs(i + 1, True) - nums[i] 18 | cache[key] = max(buy, nothing) 19 | return cache[key] 20 | 21 | return dfs(0, False) 22 | 23 | 24 | for nums in [ 25 | [1, 2, 3, 0, 2], 26 | [1] 27 | ]: 28 | print(main(nums)) 29 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/buy-and-sell-stock/best-time-to-buy-and-sell-stock-with-transaction-fee-714-(∞ transactions).py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(nums, fee): 3 | n = len(nums) 4 | cache = {} 5 | 6 | def dfs(i, bought): 7 | if i == n: 8 | return 0 9 | key = i, bought 10 | if key in cache: 11 | return cache[key] 12 | nothing = dfs(i + 1, bought) 13 | if bought: 14 | sell = dfs(i + 1, False) + nums[i] - fee 15 | cache[key] = max(sell, nothing) 16 | return cache[key] 17 | buy = dfs(i + 1, True) - nums[i] 18 | cache[key] = max(buy, nothing) 19 | return cache[key] 20 | 21 | return dfs(0, False) 22 | 23 | 24 | for nums, fee in [ 25 | ([1, 3, 2, 8, 4, 9], 2), 26 | ([1, 3, 7, 5, 10, 3], 3) 27 | ]: 28 | print(main(nums, fee)) 29 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/flip-string-to-monotone-string-926.py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(s): 3 | res = ones = 0 4 | for char in s: 5 | if char == '1': 6 | ones += 1 7 | else: 8 | res = min(ones, res + 1) 9 | return res 10 | 11 | 12 | for s in [ 13 | '00110', 14 | '010110', 15 | '00011000' 16 | ]: 17 | print(main(s)) 18 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/house-robber/house-robber-II-213.py: -------------------------------------------------------------------------------- 1 | def main(nums): 2 | n = len(nums) 3 | if n == 1: 4 | return nums[0] 5 | 6 | def helper(nums): 7 | cache = {} 8 | 9 | def dfs(i): 10 | if i >= n - 1: 11 | return 0 12 | if i in cache: 13 | return cache[i] 14 | cache[i] = max(nums[i] + dfs(i + 2), dfs(i + 1)) 15 | return cache[i] 16 | 17 | return dfs(0) 18 | 19 | return max(helper(nums[:n - 1]), helper(nums[1:])) 20 | 21 | 22 | for nums in [ 23 | [2, 3, 2], 24 | [1, 2, 3, 1], 25 | [0], 26 | [1], 27 | [1, 2] 28 | ]: 29 | print(main(nums)) 30 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/house-robber/house-robber-III-337.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | 4 | # T=n,S=n 5 | def main(root): 6 | cache = {None: 0} 7 | 8 | def dfs(root): 9 | if root in cache: 10 | return cache[root] 11 | inc = root.val 12 | exc = 0 13 | for child in [root.left, root.right]: 14 | if child: 15 | inc += dfs(child.left) + dfs(child.right) 16 | exc += dfs(child) 17 | cache[root] = max(inc, exc) 18 | return cache[root] 19 | 20 | return dfs(root) 21 | 22 | 23 | for root in [ 24 | build2([3, 2, 3, None, 3, None, 1]), 25 | build2([3, 4, 5, 1, 3, None, 1]) 26 | ]: 27 | print(main(root)) 28 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/paint-house/count.py: -------------------------------------------------------------------------------- 1 | def x(n, k): 2 | def dfs(i): 3 | if i == 1: 4 | return k 5 | return (k - 1) * dfs(i - 1) 6 | 7 | return dfs(n) 8 | 9 | 10 | def y(n, k): 11 | cache = [0] * n 12 | cache[0] = k 13 | for i in range(1, n): 14 | cache[i] = (k - 1) * cache[i - 1] 15 | return cache[-1] 16 | 17 | 18 | for n, k in [ 19 | (4, 3), 20 | (3, 3), 21 | (1, 3) 22 | ]: 23 | print(x(n, k), end=' ') 24 | print(y(n, k)) 25 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/remove-adjacent-duplicates.py: -------------------------------------------------------------------------------- 1 | def main(p): 2 | n = len(p) 3 | i = 0 4 | res = [] 5 | while i < n: 6 | res.append(p[i]) 7 | while i + 1 < n and p[i] == p[i + 1] == '*': 8 | i += 1 9 | i += 1 10 | return ''.join(res) 11 | 12 | 13 | for p in [ 14 | 'a***b', 'a***b*****', '*', '**' 15 | ]: 16 | print(main(p)) 17 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/string-equality/minimum-deletions-and-insertions-to-transform-a-string-into-another.py: -------------------------------------------------------------------------------- 1 | def x(s1, s2): 2 | def dfs(s1, s2): 3 | if not s2: 4 | return len(s1) 5 | if not s1: 6 | return len(s2) 7 | if s1[0] != s2[0]: 8 | return 1 + min(dfs(s1[1:], s2), dfs(s1, s2[1:])) 9 | return dfs(s1[1:], s2[1:]) 10 | 11 | return dfs(s1, s2) 12 | 13 | 14 | def y(s1, s2): 15 | m, n = len(s1), len(s2) 16 | 17 | def lcs(s1, s2): 18 | return 1 19 | 20 | lcs = lcs(s1, s2) 21 | minInsertions = m - lcs 22 | minDeletions = n - lcs 23 | print(minInsertions, minDeletions) 24 | 25 | 26 | for s1, s2 in [ 27 | ('abc', 'fbc'), 28 | ('abdca', 'cbda'), 29 | ('passport', 'ppsspt') 30 | ]: 31 | print(x(s1, s2)) 32 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/subarray/longest-continuous-increasing-subsequence-674-(longest-increasing-subarray).py: -------------------------------------------------------------------------------- 1 | # T=n²,S=1 2 | def x(nums): 3 | n = len(nums) 4 | res = 1 5 | for i in range(n): 6 | j = i + 1 7 | size = 1 8 | while j < n and nums[j - 1] < nums[j]: 9 | size += 1 10 | j += 1 11 | res = max(res, size) 12 | return res 13 | 14 | 15 | # T=n,S=1 16 | def y(nums): 17 | n = len(nums) 18 | i = 0 19 | res = 1 20 | while i < n: 21 | j = i + 1 22 | size = 1 23 | while j < n and nums[j - 1] < nums[j]: 24 | size += 1 25 | j += 1 26 | res = max(res, size) 27 | i = j 28 | return res 29 | 30 | 31 | for nums in [ 32 | [1, 3, 5, 4, 7], 33 | [2, 2, 2, 2, 2] 34 | ]: 35 | print(x(nums), end=' ') 36 | print(y(nums)) 37 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/subarray/max-sum-of-subarrays-from-start.py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(nums): 3 | n = len(nums) 4 | res = float('-inf') 5 | sum = 0 6 | for i in range(n): 7 | sum += nums[i] 8 | res = max(res, sum) 9 | return res 10 | 11 | 12 | for nums in [ 13 | [1], 14 | [-1, -2, -3], 15 | [5, 4, -1, 7, 8], 16 | [-2, 1, -3, 4, -1, 2, 1, -5, 4], 17 | ]: 18 | print(main(nums)) 19 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/subarray/palindrome/minimum-insertion-steps-to-make-a-string-palindrome-1312.py: -------------------------------------------------------------------------------- 1 | # f(s) = len(s) - LPS(s) 2 | # LPS(s) = LCS(s,reverse(s)) 3 | # T=n²,S=n² 4 | def main(s): 5 | n = len(s) 6 | cache = {} 7 | 8 | def dfs(i, j): 9 | if i == n or j == -1: 10 | return 0 11 | key = i, j 12 | if key in cache: 13 | return cache[key] 14 | if s[i] == s[j]: 15 | cache[key] = 1 + dfs(i + 1, j - 1) 16 | return cache[key] 17 | cache[key] = max(dfs(i + 1, j), dfs(i, j - 1)) 18 | return cache[key] 19 | 20 | return n - dfs(0, n - 1) 21 | 22 | 23 | for s in [ 24 | 'zzazz', 25 | 'mbadm', 26 | 'leetcode' 27 | ]: 28 | print(main(s)) 29 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/subarray/subarrays.py: -------------------------------------------------------------------------------- 1 | # T=n³ 2 | def main(nums): 3 | n = len(nums) 4 | res = [] 5 | for i in range(n): 6 | for j in range(i, n): 7 | res.append(nums[i:j + 1]) 8 | return res 9 | 10 | 11 | for nums in [ 12 | [], [1], [1, 2, 3] 13 | ]: 14 | print(main(nums)) 15 | 16 | print() 17 | 18 | 19 | # T=n³ 20 | def main(s): 21 | n = len(s) 22 | res = [] 23 | for i in range(n): 24 | for j in range(i, n): 25 | res.append(s[i:j + 1]) 26 | return res 27 | 28 | 29 | for s in [ 30 | '', 'a', 'abc' 31 | ]: 32 | print(main(s)) 33 | -------------------------------------------------------------------------------- /dynamic-programming/leetcode/subsequence/longest/increasing/minimum-deletions-to-make-a-sequence-sorted.py: -------------------------------------------------------------------------------- 1 | # T=n²,S=n 2 | # cache[i] length of LIS ending with nums[i] 3 | def main(nums): 4 | n = len(nums) 5 | cache = [1] * n 6 | for i in range(1, n): 7 | for j in range(i): 8 | if nums[j] < nums[i]: 9 | cache[i] = max(cache[i], 1 + cache[j]) 10 | return n - max(cache) 11 | 12 | 13 | for nums in [ 14 | [4, 2, 3, 6, 10, 1, 12], 15 | [-4, 10, 3, 7, 15], 16 | [3, 2, 1, 0] 17 | ]: 18 | print(main(nums)) 19 | -------------------------------------------------------------------------------- /fun.py: -------------------------------------------------------------------------------- 1 | a = '/a/b/c' 2 | 3 | print(a.split('/')[1:]) 4 | 5 | print('/'.split('/')[1:]) 6 | print('/a'.split('/')[1:]) 7 | 8 | grid = [[0] * 3] * 3 9 | print(grid) 10 | 11 | grid[0][0] = 4 12 | print(grid) -------------------------------------------------------------------------------- /generic/bottom-up/height.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | from collections import deque 3 | 4 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 5 | 6 | 7 | def dfs(root): 8 | res = 0 9 | for child in root.children: 10 | res = max(res, 1 + dfs(child)) 11 | return res 12 | 13 | 14 | def bfs(root): 15 | res = -1 16 | queue = deque([root]) 17 | while queue: 18 | for _ in range(len(queue)): 19 | queue.extend(queue.popleft().children) 20 | res += 1 21 | return res 22 | 23 | 24 | print(dfs(root)) 25 | print(bfs(root)) 26 | -------------------------------------------------------------------------------- /generic/bottom-up/maximum-value-leaf-node.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | 3 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 4 | 5 | 6 | def dfs(root): 7 | if not root.children: 8 | return root.val 9 | res = float('-inf') 10 | for child in root.children: 11 | res = max(res, dfs(child)) 12 | return res 13 | 14 | 15 | print(dfs(root)) 16 | -------------------------------------------------------------------------------- /generic/bottom-up/nodes-at-distance-k-from-root.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | 3 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 4 | 5 | 6 | def main(root, k): 7 | def dfs(root, k): 8 | if not k: 9 | return [root.val] 10 | res = [] 11 | for child in root.children: 12 | res += dfs(child, k - 1) 13 | return res 14 | 15 | return dfs(root, k) 16 | 17 | 18 | for k in [2, 3]: 19 | print(main(root, k)) 20 | -------------------------------------------------------------------------------- /generic/bottom-up/root-to-leaf-max-cost-path.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | 3 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 4 | 5 | 6 | def dfs(root): 7 | if not root.children: 8 | return root.val 9 | res = float('-inf') 10 | for child in root.children: 11 | res = max(res, root.val + dfs(child)) 12 | return res 13 | 14 | 15 | print(dfs(root)) 16 | -------------------------------------------------------------------------------- /generic/bottom-up/root-to-leaf-min-cost-path.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | 3 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 4 | 5 | 6 | def dfs(root): 7 | if not root.children: 8 | return root.val 9 | res = float('inf') 10 | for child in root.children: 11 | res = min(res, root.val + dfs(child)) 12 | return res 13 | 14 | 15 | print(dfs(root)) 16 | -------------------------------------------------------------------------------- /generic/bottom-up/root-to-leaf-paths-sum.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | 3 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 4 | 5 | 6 | def dfs(root): 7 | if not root.children: 8 | return [root.val] 9 | res = [] 10 | for child in root.children: 11 | for sum in dfs(child): 12 | res.append(root.val + sum) 13 | return res 14 | 15 | 16 | print(dfs(root)) 17 | -------------------------------------------------------------------------------- /generic/bottom-up/root-to-leaf-paths.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | 3 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 4 | 5 | 6 | def dfs(root): 7 | if not root.children: 8 | return [[root.val]] 9 | res = [] 10 | for child in root.children: 11 | for path in dfs(child): 12 | res.append([root.val] + path) 13 | return res 14 | 15 | 16 | print(dfs(root)) 17 | -------------------------------------------------------------------------------- /generic/bottom-up/root-to-node-path.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | 3 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 4 | 5 | 6 | def main(root, val): 7 | def dfs(root): 8 | if root.val == val: 9 | return [root.val] 10 | res = [] 11 | for child in root.children: 12 | path = dfs(child) 13 | if path: 14 | res = [root.val] + path 15 | return res 16 | 17 | return dfs(root) 18 | 19 | 20 | for val in [11, 100]: 21 | print(main(root, val)) 22 | -------------------------------------------------------------------------------- /generic/bottom-up/root-to-node-paths-at-k-distance.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | 3 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 4 | 5 | 6 | def main(root, k): 7 | def dfs(root, k): 8 | if not k: 9 | return [[root.val]] 10 | res = [] 11 | for child in root.children: 12 | for path in dfs(child, k - 1): 13 | res.append([root.val] + path) 14 | return res 15 | 16 | return dfs(root, k) 17 | 18 | 19 | for k in [2, 3]: 20 | print(main(root, k)) 21 | -------------------------------------------------------------------------------- /generic/bottom-up/root-to-node-paths-of-size-k.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | 3 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 4 | 5 | 6 | def main(root, k): 7 | def dfs(root, k): 8 | if not k - 1: 9 | return [[root.val]] 10 | res = [] 11 | for child in root.children: 12 | for path in dfs(child, k - 1): 13 | res.append([root.val] + path) 14 | return res 15 | 16 | return dfs(root, k) 17 | 18 | 19 | for k in [2, 3]: 20 | print(main(root, k)) -------------------------------------------------------------------------------- /generic/display.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | 3 | 4 | def main(root): 5 | def dfs(root): 6 | print(root.val, end=' ') 7 | for child in root.children: 8 | dfs(child) 9 | 10 | dfs(root) 11 | 12 | 13 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 14 | main(root) 15 | -------------------------------------------------------------------------------- /generic/preorder.py: -------------------------------------------------------------------------------- 1 | from util import build 2 | 3 | 4 | def main(root): 5 | def dfs(root): 6 | res = [root.val] 7 | for child in root.children: 8 | res += dfs(child) 9 | return res 10 | 11 | return dfs(root) 12 | 13 | 14 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 15 | print(main(root)) 16 | -------------------------------------------------------------------------------- /generic/top-down/preorder.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | 3 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 4 | 5 | 6 | def dfs(root): 7 | print(root.val, end=' ') 8 | for child in root.children: 9 | dfs(child) 10 | 11 | 12 | print(dfs(root)) 13 | -------------------------------------------------------------------------------- /generic/top-down/root-to-leaf-paths.py: -------------------------------------------------------------------------------- 1 | from generic.util import build 2 | 3 | root = build([1, 2, 5, -1, 6, -1, -1, 3, 7, -1, 8, 11, -1, 12, -1, -1, 9, -1, -1, 4, 10, -1, -1, -1]) 4 | 5 | 6 | def main(root): 7 | def dfs(root, path): 8 | if not root.children: 9 | return [path] 10 | res = [] 11 | for child in root.children: 12 | res += dfs(child, path + [child.data]) 13 | return res 14 | 15 | return dfs(root, [1]) 16 | 17 | 18 | print(main(root)) 19 | 20 | 21 | def main(root): 22 | def dfs(root, path): 23 | if not root.children: 24 | return [path[:]] 25 | res = [] 26 | for child in root.children: 27 | path.append(child.data) 28 | res += dfs(child, path) 29 | path.pop() 30 | return res 31 | 32 | return dfs(root, [1]) 33 | 34 | 35 | print(main(root)) 36 | -------------------------------------------------------------------------------- /generic/util.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, val): 3 | self.val = val 4 | self.children = [] 5 | 6 | 7 | class Tree: 8 | def __init__(self): 9 | self.root = None 10 | 11 | 12 | def build(lst): 13 | tree = Tree() 14 | node = Node(lst[0]) 15 | tree.root = node 16 | stack = [node] 17 | for i in range(1, len(lst)): 18 | if lst[i] == -1: 19 | stack.pop() 20 | continue 21 | node = Node(lst[i]) 22 | stack[-1].children.append(node) 23 | stack.append(node) 24 | return tree.root 25 | -------------------------------------------------------------------------------- /graphs/leetcode/connected-components/number-of-connected-components-in-an-undirected-graph-323.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from graphs.util import build2 3 | 4 | 5 | # T=v+e,S=v 6 | def main(n, edges): 7 | graph = build2(n, edges) 8 | vis = set() 9 | res = 0 10 | 11 | def dfs(src): 12 | vis.add(src) 13 | for nbr in graph[src]: 14 | if nbr not in vis: 15 | dfs(nbr) 16 | 17 | for src in graph: 18 | if src not in vis: 19 | dfs(src) 20 | res += 1 21 | 22 | return res 23 | 24 | 25 | for n, edges in [ 26 | (5, [[0, 1], [1, 2], [3, 4]]), 27 | (5, [[0, 1], [1, 2], [2, 3], [3, 4]]) 28 | ]: 29 | print(main(n, edges)) 30 | -------------------------------------------------------------------------------- /graphs/leetcode/connected-components/number-of-operations-to-make-network-connected-1319.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from graphs.util import build2 3 | 4 | 5 | def main(n, edges): 6 | if len(edges) < n - 1: 7 | return -1 8 | graph = build2(n, edges) 9 | vis = set() 10 | res = 0 11 | 12 | def dfs(src): 13 | vis.add(src) 14 | for nbr in graph[src]: 15 | if nbr not in vis: 16 | dfs(nbr) 17 | 18 | for src in graph: 19 | if src not in vis: 20 | dfs(src) 21 | res += 1 22 | 23 | return res - 1 24 | 25 | 26 | for n, edges in [ 27 | (4, [[0, 1], [0, 2], [1, 2]]), 28 | (6, [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3]]), 29 | (6, [[0, 1], [0, 2], [0, 3], [1, 2]]) 30 | ]: 31 | print(main(n, edges)) 32 | -------------------------------------------------------------------------------- /graphs/leetcode/connected-components/number-of-provinces-547.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | 4 | def main(grid): 5 | graph = {i: [] for i in range(len(grid))} 6 | for i in range(len(grid)): 7 | for j in range(i + 1, len(grid)): 8 | if grid[i][j]: 9 | graph[i].append(j) 10 | graph[j].append(i) 11 | 12 | vis = set() 13 | res = 0 14 | 15 | def dfs(src): 16 | vis.add(src) 17 | for nbr in graph[src]: 18 | if nbr not in vis: 19 | dfs(nbr) 20 | 21 | for src in graph: 22 | if src not in vis: 23 | dfs(src) 24 | res += 1 25 | 26 | return res 27 | 28 | 29 | for grid in [ 30 | [ 31 | [1, 1, 0], 32 | [1, 1, 0], 33 | [0, 0, 1] 34 | ], 35 | [ 36 | [1, 0, 0], 37 | [0, 1, 0], 38 | [0, 0, 1] 39 | ] 40 | ]: 41 | print(main(grid)) 42 | -------------------------------------------------------------------------------- /graphs/leetcode/practice.py: -------------------------------------------------------------------------------- 1 | def main(): 2 | s = 'nik' 3 | for i in range(len(s)): 4 | for char in 'abcdefghijklmnopqrstuvwxyz': 5 | print(s[:i] + char + s[i + 1:], end='|') 6 | print() 7 | 8 | 9 | main() 10 | -------------------------------------------------------------------------------- /graphs/leetcode/reconstruct-itinerary-332.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from heapq import * 3 | 4 | 5 | def main(edges): 6 | res = [] 7 | graph = defaultdict(list) 8 | 9 | def dfs(src): 10 | while graph[src]: 11 | dfs(heappop(graph[src])) 12 | res.append(src) 13 | 14 | for src, dest in edges: 15 | heappush(graph[src], dest) 16 | dfs('JFK') 17 | return res[::-1] 18 | 19 | 20 | for edges in [ 21 | [['MUC', 'LHR'], ['JFK', 'MUC'], ['SFO', 'SJC'], ['LHR', 'SFO']], 22 | [['JFK', 'SFO'], ['JFK', 'ATL'], ['SFO', 'ATL'], ['ATL', 'JFK'], ['ATL', 'SFO']], 23 | [['JFK', 'KUL'], ['JFK', 'NRT'], ['NRT', 'JFK']] 24 | ]: 25 | print(main(edges)) 26 | -------------------------------------------------------------------------------- /graphs/practice/count-connected-components.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from graphs.util import build 3 | 4 | 5 | def main(edges): 6 | graph = build(edges) 7 | vis = set() 8 | res = 0 9 | 10 | def dfs(src): 11 | vis.add(src) 12 | for nbr in graph[src]: 13 | if nbr not in vis: 14 | dfs(nbr) 15 | 16 | for src in graph: 17 | if src not in vis: 18 | dfs(src) 19 | res += 1 20 | 21 | return res 22 | 23 | 24 | for edges in [ 25 | [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (0, 3), (4, 6)], 26 | [(0, 1), (2, 3), (4, 5), (4, 6), (5, 6)] 27 | ]: 28 | print(main(edges)) 29 | -------------------------------------------------------------------------------- /graphs/practice/cycle/directed.py: -------------------------------------------------------------------------------- 1 | from graphs.util import build3 2 | 3 | 4 | # T=v+e,S=v 5 | def main(n, edges): 6 | graph = build3(n, edges) 7 | vis = set() 8 | path = set() 9 | 10 | def dfs(src): 11 | vis.add(src) 12 | path.add(src) 13 | for nbr in graph[src]: 14 | if nbr in path: 15 | return True 16 | if nbr not in vis: 17 | if dfs(nbr): 18 | return True 19 | path.remove(src) 20 | return False 21 | 22 | for src in range(n): 23 | if src not in vis: 24 | if dfs(src): 25 | return True 26 | return False 27 | 28 | 29 | for n, edges in [ 30 | (2, [(1, 0)]), 31 | (5, [(0, 1), (1, 2), (2, 3), (3, 4), (4, 1)]), 32 | (5, [(0, 1), (1, 2), (2, 3), (3, 4), (1, 4)]), 33 | (10, [(0, 1), (2, 3), (3, 4), (4, 5), (6, 7), (7, 8), (8, 9), (9, 6)]) 34 | ]: 35 | print(main(n, edges)) 36 | -------------------------------------------------------------------------------- /graphs/practice/cycle/undirected.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from graphs.util import build2 3 | 4 | 5 | # T=v+e,S=v 6 | def main(n, edges): 7 | graph = build2(n, edges) 8 | vis = set() 9 | 10 | def dfs(src, parent): 11 | vis.add(src) 12 | for nbr in graph[src]: 13 | if nbr not in vis: 14 | if dfs(nbr, src): 15 | return True 16 | elif nbr != parent: 17 | return True 18 | return False 19 | 20 | for src in graph: 21 | if src not in vis: 22 | if dfs(src, -1): 23 | return True 24 | 25 | return False 26 | 27 | 28 | for n, edges in [ 29 | (7, [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (0, 3), (4, 6)]), 30 | (7, [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]), 31 | (7, [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]), 32 | ]: 33 | print(main(n, edges)) 34 | -------------------------------------------------------------------------------- /graphs/practice/dfs/dfs-in-grid.py: -------------------------------------------------------------------------------- 1 | def outside(m, n, ri, ci): 2 | return ri in [-1, m] or ci in [-1, n] 3 | 4 | 5 | def main(grid): 6 | m, n = len(grid), len(grid[0]) 7 | offsets = (-1, 0), (0, 1), (1, 0), (0, -1) 8 | vis = set() 9 | 10 | def dfs(ri, ci): 11 | if outside(m, n, ri, ci) or (ri, ci) in vis: 12 | return 13 | vis.add((ri, ci)) 14 | print(grid[ri][ci], end=' ') 15 | for i, j in offsets: 16 | dfs(ri + i, ci + j) 17 | 18 | dfs(0, 0) 19 | 20 | 21 | for grid in [ 22 | [ 23 | [1, 2, 3], 24 | [4, 5, 6], 25 | [7, 8, 9] 26 | ], 27 | [ 28 | [1, 2, 3, 4], 29 | [5, 6, 7, 8], 30 | [9, 10, 11, 12], 31 | [13, 14, 15, 16] 32 | ], 33 | [ 34 | [-1, 2, 3], 35 | [0, 9, 8], 36 | [1, 0, 1] 37 | ] 38 | ]: 39 | main(grid) 40 | print() 41 | -------------------------------------------------------------------------------- /graphs/practice/dfs/dfs.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from graphs.util import build 3 | 4 | 5 | # T=v+e,S=v 6 | def main(edges): 7 | graph = build(edges) 8 | vis = set() 9 | 10 | def dfs(src): 11 | vis.add(src) 12 | print(src, end=' ') 13 | for nbr in graph[src]: 14 | if nbr not in vis: 15 | dfs(nbr) 16 | 17 | for src in graph: 18 | if src not in vis: 19 | dfs(src) 20 | print() 21 | 22 | 23 | for edges in [ 24 | [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (0, 3), (4, 6)], 25 | [(0, 1), (2, 3), (4, 5), (4, 6), (5, 6)] 26 | ]: 27 | main(edges) 28 | print() 29 | -------------------------------------------------------------------------------- /graphs/practice/dfs/preorder.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from graphs.util import build 3 | 4 | 5 | def main(edges): 6 | res = [] 7 | graph = build(edges) 8 | vis = set() 9 | 10 | def dfs(src): 11 | vis.add(src) 12 | res = [src] 13 | for nbr in graph[src]: 14 | if nbr not in vis: 15 | res += dfs(nbr) 16 | return res 17 | 18 | for src in graph: 19 | if src not in vis: 20 | res.append(dfs(src)) 21 | return res 22 | 23 | 24 | for edges in [ 25 | [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (0, 3), (4, 6)], 26 | [(0, 1), (2, 3), (4, 5), (4, 6), (5, 6)] 27 | ]: 28 | print(main(edges)) 29 | -------------------------------------------------------------------------------- /graphs/practice/get-connected-components.py: -------------------------------------------------------------------------------- 1 | from graphs.util import build 2 | 3 | inputs = [ 4 | [(0, 1), (2, 3), (4, 5), (4, 6), (5, 6)] 5 | ] 6 | 7 | 8 | def main(edges): 9 | graph = build(edges) 10 | vis = set() 11 | res = [] 12 | 13 | def dfs(src): 14 | vis.add(src) 15 | path.append(src) 16 | for nbr in graph[src]: 17 | if nbr not in vis: 18 | dfs(nbr) 19 | 20 | for src in graph: 21 | if src not in vis: 22 | path = [] 23 | dfs(src) 24 | res.append(path) 25 | 26 | return res 27 | 28 | 29 | for edges in inputs: 30 | print(main(edges)) 31 | -------------------------------------------------------------------------------- /graphs/practice/hamiltonian-path-and-cycle.py: -------------------------------------------------------------------------------- 1 | from graphs.util import build2 2 | 3 | 4 | def main(n, edges): 5 | graph = build2(n, edges) 6 | vis = set() 7 | 8 | def dfs(src): 9 | vis.add(src) 10 | if len(vis) == n: 11 | vis.remove(src) 12 | return [[src]] 13 | res = [] 14 | for nbr in graph[src]: 15 | if nbr not in vis: 16 | for path in dfs(nbr): 17 | res.append([src] + path) 18 | vis.remove(src) 19 | return res 20 | 21 | res = dfs(0) 22 | for path in res: 23 | if path[-1] in graph[path[0]]: 24 | path.append('*') 25 | else: 26 | path.append('.') 27 | return res 28 | 29 | 30 | for n, edges in [ 31 | (7, [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (0, 3), (2, 5), (4, 6)]), 32 | ]: 33 | print(main(n, edges)) 34 | -------------------------------------------------------------------------------- /graphs/practice/heap/src-to-all-vertices-min-weight-path.py: -------------------------------------------------------------------------------- 1 | from heapq import heappush, heappop 2 | from graphs.util import build4 3 | 4 | 5 | def main(n, edges): 6 | res = 0 7 | graph = build4(n, edges) 8 | heap = [] 9 | heappush(heap, (0, 0, [0])) 10 | vis = set() 11 | while heap: 12 | weightSoFar, src, pathSoFar = heappop(heap) 13 | if src in vis: 14 | continue 15 | vis.add(src) 16 | print(src, pathSoFar, weightSoFar) 17 | for nbr, weight in graph[src]: 18 | if nbr not in vis: 19 | heappush(heap, (weightSoFar + weight, nbr, pathSoFar + [nbr])) 20 | 21 | 22 | for n, edges in [ 23 | (7, [(0, 1, 10), (1, 2, 10), (2, 3, 10), (3, 4, 2), (4, 5, 3), (5, 6, 3), (0, 3, 40), (4, 6, 8)]), 24 | (7, [(0, 1, 10), (1, 2, 10), (2, 3, 10), (3, 4, 2), (4, 5, 3), (5, 6, 3), (0, 3, 40), (4, 6, 8), (2, 5, 5)]) 25 | ]: 26 | main(n, edges) 27 | print() 28 | -------------------------------------------------------------------------------- /graphs/practice/iterative-traversal.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from graphs.util import build 3 | 4 | 5 | def main(edges): 6 | graph = build(edges) 7 | vis = set() 8 | 9 | def dfs(src): 10 | stack = [src] 11 | vis.add(src) 12 | while stack: 13 | src = stack.pop() 14 | print(src, end=' ') 15 | for nbr in reversed(graph[src]): 16 | if nbr not in vis: 17 | vis.add(nbr) 18 | stack.append(nbr) 19 | 20 | for src in graph: 21 | if src not in vis: 22 | dfs(src) 23 | print() 24 | 25 | 26 | for edges in [ 27 | [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (0, 3), (4, 6)], 28 | [(0, 1), (2, 3), (4, 5), (4, 6), (5, 6)] 29 | ]: 30 | main(edges) 31 | print() 32 | -------------------------------------------------------------------------------- /graphs/practice/knights-tour/count-possible-moves.py: -------------------------------------------------------------------------------- 1 | def outside(ri, ci, m, n): 2 | return ri < 0 or ci < 0 or ri > m - 1 or ci > n - 1 3 | 4 | 5 | def main(grid, sri, sci): 6 | res = 0 7 | m, n = len(grid), len(grid[0]) 8 | offsets = (-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1) 9 | for i, j in offsets: 10 | if outside(sri + i, sci + j, m, n) or grid[sri + i][sci + j]: 11 | continue 12 | res += 1 13 | return res 14 | 15 | 16 | for grid, sri, sci in [( 17 | [ 18 | [1, 0, 1, 0], 19 | [0, 1, 1, 1], 20 | [1, 1, 0, 1], 21 | [0, 1, 1, 1] 22 | ], 23 | 2, 2) 24 | ]: 25 | print(main(grid, sri, sri)) 26 | -------------------------------------------------------------------------------- /graphs/practice/knights-tour/visit-all/count-all-configs.py: -------------------------------------------------------------------------------- 1 | def outside(n, ri, ci): 2 | return ri > n - 1 or ci > n - 1 or ri < 0 or ci < 0 3 | 4 | 5 | # T=8ⁿ²,S=n² 6 | def main(n, sri, sci): 7 | grid = [[0 for _ in range(n)] for _ in range(n)] 8 | offsets = (-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1) 9 | 10 | def dfs(ri, ci, move): 11 | if outside(n, ri, ci) or grid[ri][ci]: 12 | return 0 13 | grid[ri][ci] = move 14 | if move == n * n: 15 | grid[ri][ci] = 0 16 | return 1 17 | res = 0 18 | for i, j in offsets: 19 | res += dfs(ri + i, ci + j, move + 1) 20 | grid[ri][ci] = 0 21 | return res 22 | 23 | return dfs(sri, sci, 1) 24 | 25 | 26 | for n, sri, sci in [ 27 | (5, 2, 0), 28 | (5, 0, 0), 29 | (4, 0, 0), 30 | (4, 2, 0) 31 | ]: 32 | print(main(n, sri, sci)) 33 | -------------------------------------------------------------------------------- /graphs/practice/knights-tour/visit-all/print-all-configs.py: -------------------------------------------------------------------------------- 1 | def outside(n, ri, ci): 2 | return ri > n - 1 or ci > n - 1 or ri < 0 or ci < 0 3 | 4 | 5 | # T=8ⁿ²,S=n² 6 | def main(n, sri, sci): 7 | grid = [[0 for _ in range(n)] for _ in range(n)] 8 | offsets = (-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1) 9 | 10 | def dfs(ri, ci, move): 11 | if outside(n, ri, ci) or grid[ri][ci]: 12 | return 13 | grid[ri][ci] = move 14 | if move == n * n: 15 | print(grid) 16 | grid[ri][ci] = 0 17 | return 18 | for i, j in offsets: 19 | dfs(ri + i, ci + j, move + 1) 20 | grid[ri][ci] = 0 21 | 22 | dfs(sri, sci, 1) 23 | 24 | 25 | for n, sri, sci in [ 26 | (5, 2, 0), 27 | (5, 0, 0), 28 | (4, 0, 0), 29 | (4, 2, 0) 30 | ]: 31 | main(n, sri, sci) 32 | -------------------------------------------------------------------------------- /graphs/practice/knights-tour/visit-all/print-one-config.py: -------------------------------------------------------------------------------- 1 | def outside(n, ri, ci): 2 | return ri > n - 1 or ci > n - 1 or ri < 0 or ci < 0 3 | 4 | 5 | def main(n, sri, sci): 6 | grid = [[0 for _ in range(n)] for _ in range(n)] 7 | offsets = (-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1) 8 | 9 | def dfs(ri, ci, move): 10 | if outside(n, ri, ci) or grid[ri][ci]: 11 | return False 12 | grid[ri][ci] = move 13 | if move == n * n: 14 | print(grid) 15 | return True 16 | for i, j in offsets: 17 | if dfs(ri + i, ci + j, move + 1): 18 | return True 19 | grid[ri][ci] = 0 20 | return False 21 | 22 | dfs(sri, sci, 1) 23 | 24 | 25 | for n, sri, sci in [(5, 2, 0), (5, 0, 0), (4, 0, 0), (4, 2, 0)]: 26 | main(n, sri, sci) 27 | -------------------------------------------------------------------------------- /graphs/practice/perfect-friends.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from graphs.util import build 3 | 4 | 5 | def main(edges): 6 | res = 0 7 | graph = build(edges) 8 | vis = set() 9 | 10 | def dfs(src): 11 | vis.add(src) 12 | res = 1 13 | for nbr in graph[src]: 14 | if nbr not in vis: 15 | res += dfs(nbr) 16 | return res 17 | 18 | count = [] 19 | for src in graph: 20 | if src not in vis: 21 | count.append(dfs(src)) 22 | for i in range(len(count)): 23 | res += count[i] * sum(count[i + 1:]) 24 | return res 25 | 26 | 27 | for edges in [ 28 | [(0, 1), (2, 3), (4, 5), (5, 6), (4, 6)] 29 | ]: 30 | print(main(edges)) 31 | -------------------------------------------------------------------------------- /graphs/practice/spread-of-infection.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, deque 2 | from graphs.util import build 3 | 4 | 5 | def main(edges): 6 | res = 0 7 | graph = build(edges) 8 | queue = deque([(0, 0)]) 9 | vis = {0} 10 | while queue: 11 | src, dist = queue.popleft() 12 | if dist == 2: 13 | break 14 | for nbr in graph[src]: 15 | if nbr not in vis: 16 | vis.add(nbr) 17 | queue.append((nbr, dist + 1)) 18 | res += 1 19 | return res 20 | 21 | 22 | for edges in [ 23 | [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (0, 3), (4, 6)], 24 | ]: 25 | print(main(edges)) 26 | -------------------------------------------------------------------------------- /graphs/practice/src-dest/bfs/src-to-dest-path-bfs.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, deque 2 | from graphs.util import build 3 | 4 | 5 | def bfs(edges, src, dest): 6 | graph = build(edges) 7 | queue = deque([(src, [src])]) 8 | while queue: 9 | src, psf = queue.popleft() 10 | if src == dest: 11 | print(psf, end=' ') 12 | return 13 | for nbr in graph[src]: 14 | if nbr not in psf: 15 | queue.append((nbr, psf + [nbr])) 16 | 17 | 18 | for edges, data in [ 19 | ([(0, 1), (0, 3), (1, 2), (3, 2), (3, 4), (4, 5), (4, 6), (5, 6)], [(0, 6), (2, 5)]), 20 | ([(0, 1), (0, 3), (1, 2), (3, 2), (4, 5), (4, 6), (5, 6)], [(0, 2), (2, 5)]), 21 | ]: 22 | for src, dest in data: 23 | bfs(edges, src, dest) 24 | print() 25 | -------------------------------------------------------------------------------- /graphs/practice/src-dest/bfs/src-to-dest-paths-bfs.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, deque 2 | from graphs.util import build 3 | 4 | 5 | def bfs(edges, src, dest): 6 | graph = build(edges) 7 | queue = deque([(src, [src])]) 8 | while queue: 9 | src, psf = queue.popleft() 10 | if src == dest: 11 | print(psf, end=' ') 12 | continue 13 | for nbr in graph[src]: 14 | if nbr not in psf: 15 | queue.append((nbr, psf + [nbr])) 16 | 17 | 18 | for edges, data in [ 19 | ([(0, 1), (0, 3), (1, 2), (3, 2), (3, 4), (4, 5), (4, 6), (5, 6)], [(0, 6), (2, 5)]), 20 | ([(0, 1), (0, 3), (1, 2), (3, 2), (4, 5), (4, 6), (5, 6)], [(0, 2), (2, 5)]), 21 | ]: 22 | for src, dest in data: 23 | bfs(edges, src, dest) 24 | print() 25 | -------------------------------------------------------------------------------- /graphs/practice/src-dest/bfs/src-to-dest-shortest-path-length.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, deque 2 | from graphs.util import build 3 | 4 | 5 | def bfs(edges, src, dest): 6 | graph = build(edges) 7 | queue = deque([(src, 0)]) 8 | vis = {src} 9 | while queue: 10 | src, dist = queue.popleft() 11 | if src == dest: 12 | return dist 13 | for nbr in graph[src]: 14 | if nbr not in vis: 15 | vis.add(nbr) 16 | queue.append((nbr, dist + 1)) 17 | return -1 18 | 19 | 20 | for edges, data in [ 21 | ([(0, 1), (0, 3), (1, 2), (3, 2), (3, 4), (4, 5), (4, 6), (5, 6)], [(0, 6), (2, 5)]), 22 | ([(0, 1), (0, 3), (1, 2), (3, 2), (4, 5), (4, 6), (5, 6)], [(0, 2), (2, 5)]), 23 | ]: 24 | for src, dest in data: 25 | print(bfs(edges, src, dest)) 26 | -------------------------------------------------------------------------------- /graphs/practice/src-dest/bottom-up/src-to-dest-path.py: -------------------------------------------------------------------------------- 1 | from graphs.util import build 2 | 3 | 4 | def main(edges, src, dest): 5 | graph = build(edges) 6 | vis = set() 7 | 8 | def dfs(src): 9 | if src == dest: 10 | return [dest] 11 | vis.add(src) 12 | for nbr in graph[src]: 13 | if nbr not in vis: 14 | path = dfs(nbr) 15 | if path: 16 | return [src] + path 17 | return [] 18 | 19 | return dfs(src) 20 | 21 | 22 | for edges, data in [ 23 | ([(0, 1), (0, 3), (1, 2), (3, 2), (3, 4), (4, 5), (4, 6), (5, 6)], [(0, 6), (2, 5)]), 24 | ([(0, 1), (0, 3), (1, 2), (3, 2), (4, 5), (4, 6), (5, 6)], [(0, 2), (2, 5)]), 25 | ]: 26 | for src, dest in data: 27 | print(main(edges, src, dest)) 28 | -------------------------------------------------------------------------------- /graphs/practice/src-dest/top-down/get-paths-2.py: -------------------------------------------------------------------------------- 1 | from graphs.util import build 2 | 3 | 4 | def main(edges, src, dest): 5 | graph = build(edges) 6 | vis = set() 7 | path = [] 8 | 9 | def dfs(src): 10 | if src == dest: 11 | return [path + [src]] 12 | path.append(src) 13 | vis.add(src) 14 | res = [] 15 | for nbr in graph[src]: 16 | if nbr not in vis: 17 | res += dfs(nbr) 18 | vis.remove(src) 19 | path.pop() 20 | return res 21 | 22 | return dfs(src) 23 | 24 | 25 | for edges, data in [ 26 | ([(0, 1), (0, 3), (1, 2), (3, 2), (3, 4), (4, 5), (4, 6), (5, 6)], [(0, 6), (2, 5)]), 27 | ([(0, 1), (0, 3), (1, 2), (3, 2), (4, 5), (4, 6), (5, 6)], [(0, 2), (2, 5)]), 28 | ]: 29 | for src, dest in data: 30 | print(main(edges, src, dest)) 31 | -------------------------------------------------------------------------------- /graphs/practice/src-dest/top-down/get-paths.py: -------------------------------------------------------------------------------- 1 | from graphs.util import build 2 | 3 | 4 | def main(edges, src, dest): 5 | graph = build(edges) 6 | vis = set() 7 | 8 | def dfs(src, path): 9 | if src == dest: 10 | return [path] 11 | vis.add(src) 12 | res = [] 13 | for nbr in graph[src]: 14 | if nbr not in vis: 15 | res += dfs(nbr, path + [nbr]) 16 | vis.remove(src) 17 | return res 18 | 19 | return dfs(src, [src]) 20 | 21 | 22 | for edges, data in [ 23 | ([(0, 1), (0, 3), (1, 2), (3, 2), (3, 4), (4, 5), (4, 6), (5, 6)], [(0, 6), (2, 5)]), 24 | ([(0, 1), (0, 3), (1, 2), (3, 2), (4, 5), (4, 6), (5, 6)], [(0, 2), (2, 5)]), 25 | ]: 26 | for src, dest in data: 27 | print(main(edges, src, dest)) 28 | -------------------------------------------------------------------------------- /graphs/practice/src-dest/top-down/print/print-paths-2.py: -------------------------------------------------------------------------------- 1 | from graphs.util import build 2 | 3 | 4 | def main(edges, src, dest): 5 | graph = build(edges) 6 | 7 | def dfs(src, vis, path): 8 | if src == dest: 9 | print(path, end=' ') 10 | return 11 | for nbr in graph[src]: 12 | if nbr not in vis: 13 | dfs(nbr, vis | {nbr}, path + [nbr]) 14 | 15 | dfs(src, {src}, [src]) 16 | 17 | 18 | for edges, data in [ 19 | ([(0, 1), (0, 3), (1, 2), (3, 2), (3, 4), (4, 5), (4, 6), (5, 6)], [(0, 6), (2, 5)]), 20 | ([(0, 1), (0, 3), (1, 2), (3, 2), (4, 5), (4, 6), (5, 6)], [(0, 2), (2, 5)]), 21 | ]: 22 | for src, dest in data: 23 | main(edges, src, dest) 24 | print() 25 | -------------------------------------------------------------------------------- /graphs/practice/src-dest/top-down/print/print-paths.py: -------------------------------------------------------------------------------- 1 | from graphs.util import build 2 | 3 | 4 | def main(edges, src, dest): 5 | graph = build(edges) 6 | vis = set() 7 | path = [] 8 | 9 | def dfs(src): 10 | vis.add(src) 11 | path.append(src) 12 | if src == dest: 13 | print(path, end=' ') 14 | path.pop() 15 | vis.remove(src) 16 | return 17 | for nbr in graph[src]: 18 | if nbr not in vis: 19 | dfs(nbr) 20 | path.pop() 21 | vis.remove(src) 22 | 23 | dfs(src) 24 | 25 | 26 | for edges, data in [ 27 | ([(0, 1), (0, 3), (1, 2), (3, 2), (3, 4), (4, 5), (4, 6), (5, 6)], [(0, 6), (2, 5)]), 28 | ([(0, 1), (0, 3), (1, 2), (3, 2), (4, 5), (4, 6), (5, 6)], [(0, 2), (2, 5)]), 29 | ]: 30 | for src, dest in data: 31 | main(edges, src, dest) 32 | print() 33 | -------------------------------------------------------------------------------- /graphs/practice/topological-sort.py: -------------------------------------------------------------------------------- 1 | from graphs.util import build3 2 | 3 | 4 | def main(n, edges): 5 | graph = build3(n, edges) 6 | vis = set() 7 | path = [] 8 | 9 | def dfs(src): 10 | vis.add(src) 11 | for nbr in graph[src]: 12 | if nbr not in vis: 13 | dfs(nbr) 14 | path.append(src) 15 | 16 | for src in graph: 17 | if src not in vis: 18 | dfs(src) 19 | 20 | return list(reversed(path)) 21 | 22 | 23 | for n, edges in [ 24 | (7, [(0, 1), (1, 2), (2, 3), (0, 3), (4, 5), (5, 6), (4, 6)]), 25 | (6, [(2, 3), (3, 1), (5, 2), (5, 0), (4, 0), (4, 1)]) 26 | ]: 27 | print(main(n, edges)) 28 | -------------------------------------------------------------------------------- /graphs/practice/undirected/is-connected.py: -------------------------------------------------------------------------------- 1 | from graphs.util import build 2 | 3 | 4 | def main(edges): 5 | graph = build(edges) 6 | vis = set() 7 | 8 | def dfs(src): 9 | vis.add(src) 10 | for nbr in graph[src]: 11 | if nbr not in vis: 12 | dfs(nbr) 13 | 14 | dfs(0) 15 | return len(vis) == len(graph) 16 | 17 | 18 | for edges in [ 19 | [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (0, 3), (4, 6)], 20 | [(0, 1), (2, 3), (4, 5), (4, 6), (5, 6)] 21 | ]: 22 | print(main(edges)) 23 | -------------------------------------------------------------------------------- /graphs/practice/visit-all/print-all-configs.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from graphs.util import build 3 | 4 | 5 | def main(edges, n): 6 | graph = build(edges) 7 | path, vis = [], set() 8 | 9 | def dfs(src): 10 | vis.add(src) 11 | path.append(src) 12 | if len(vis) == n: 13 | print(path) 14 | vis.remove(src) 15 | path.pop() 16 | return 17 | for nbr in graph[src]: 18 | if nbr not in vis: 19 | dfs(nbr) 20 | vis.remove(src) 21 | path.pop() 22 | 23 | dfs(0) 24 | 25 | 26 | for edges, n in [ 27 | ([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (0, 3), (2, 5), (4, 6)], 7), 28 | ([(0, 3), (3, 2), (2, 1), (0, 1), (3, 4), (4, 5), (4, 6), (5, 6)], 7) 29 | ]: 30 | main(edges, n) 31 | print() 32 | -------------------------------------------------------------------------------- /graphs/practice/visit-all/print-one-config.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from graphs.util import build 3 | 4 | 5 | def main(graph, n): 6 | graph = build(edges) 7 | path, vis = [], set() 8 | 9 | def dfs(src): 10 | vis.add(src) 11 | path.append(src) 12 | if len(vis) == n: 13 | return True 14 | for nbr in graph[src]: 15 | if nbr not in vis: 16 | if dfs(nbr): 17 | return True 18 | vis.remove(src) 19 | path.pop() 20 | return False 21 | 22 | dfs(0) 23 | return path 24 | 25 | 26 | for edges, n in [ 27 | ([(0, 3), (3, 2), (2, 1), (0, 1), (3, 4), (4, 5), (4, 6), (5, 6)], 7) 28 | ]: 29 | print(main(edges, n)) 30 | -------------------------------------------------------------------------------- /greedy/activity-selection.py: -------------------------------------------------------------------------------- 1 | # T=nlogn 2 | def main(nums): 3 | nums.sort(key=lambda x: x[1]) 4 | res = [] 5 | prevEnd = float('-inf') 6 | for i, (currStart, currEnd) in enumerate(nums): 7 | if currStart >= prevEnd: 8 | prevEnd = currEnd 9 | res.append(i) 10 | return res 11 | 12 | 13 | for nums in [ 14 | [[10, 20], [12, 25], [20, 30]], 15 | [[1, 2], [3, 4], [0, 6], [5, 7], [8, 9], [5, 9]], 16 | ]: 17 | print(main(nums), end='') 18 | print() 19 | -------------------------------------------------------------------------------- /greedy/leetcode/maximum-units-on-a-truck-1710.py: -------------------------------------------------------------------------------- 1 | # T=nlogn,S=1 2 | def main(boxTypes, truckSize): 3 | res = 0 4 | boxTypes.sort(key=lambda x: -x[1]) 5 | for boxCount, unitsPerBox in boxTypes: 6 | boxes = min(truckSize, boxCount) 7 | res += boxes * unitsPerBox 8 | truckSize -= boxes 9 | if not truckSize: 10 | break 11 | return res 12 | 13 | 14 | for boxTypes, truckSize in [ 15 | ([[1, 3], [2, 2], [3, 1]], 4), 16 | ([[5, 10], [2, 5], [4, 7], [3, 9]], 10), 17 | ]: 18 | print(main(boxTypes, truckSize)) 19 | -------------------------------------------------------------------------------- /greedy/leetcode/meeting-rooms-252.py: -------------------------------------------------------------------------------- 1 | inputs = [ 2 | [[0, 30], [5, 10], [15, 20]], 3 | [[7, 10], [2, 4]] 4 | ] 5 | 6 | 7 | def overlap(i1, i2): 8 | return min(i1[1], i2[1]) > max(i1[0], i2[0]) 9 | 10 | 11 | # T=n²,S=1 12 | def main(nums): 13 | n = len(nums) 14 | for i in range(n): 15 | for j in range(i + 1, n): 16 | if overlap(nums[i], nums[j]): 17 | return False 18 | return True 19 | 20 | 21 | for nums in inputs: 22 | print(main(nums)) 23 | 24 | print() 25 | 26 | 27 | # T=nlogn,S=n 28 | def main(nums): 29 | nums.sort() 30 | for i in range(len(nums) - 1): 31 | endTimeOfCurrentMeeting = nums[i][1] 32 | startTimeOfNextMeeting = nums[i + 1][0] 33 | if endTimeOfCurrentMeeting > startTimeOfNextMeeting: 34 | return False 35 | return True 36 | 37 | 38 | for nums in inputs: 39 | print(main(nums)) 40 | -------------------------------------------------------------------------------- /greedy/leetcode/merge-intervals-56.py: -------------------------------------------------------------------------------- 1 | # T=nlogn,S=n 2 | def main(nums): 3 | nums.sort() 4 | res = [nums[0]] 5 | for i in range(1, len(nums)): 6 | startTimeOfNextInterval, endTimeOfNextInterval = nums[i] 7 | endTimeOfCurrentInterval = res[-1][1] 8 | if startTimeOfNextInterval > endTimeOfCurrentInterval: 9 | res.append(nums[i]) 10 | else: 11 | res[-1][1] = max(endTimeOfCurrentInterval, endTimeOfNextInterval) 12 | return res 13 | 14 | 15 | for nums in [ 16 | [[1, 3], [2, 6], [8, 10], [15, 18]], 17 | [[1, 4], [4, 5]], 18 | [[1, 3], [2, 4], [5, 7], [6, 8]], 19 | [[1, 4], [2, 3]], 20 | [[10, 16], [2, 8], [1, 6], [7, 12]], 21 | [[1, 2], [2, 3], [3, 4], [1, 3]] 22 | ]: 23 | print(main(nums), end='') 24 | print() 25 | -------------------------------------------------------------------------------- /greedy/leetcode/minimum/minimum-deletion-cost-to-avoid-repeating-letters-1578.py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(s, cost): 3 | res = 0 4 | for i in range(1, len(s)): 5 | if s[i] == s[i - 1]: 6 | res += min(cost[i], cost[i - 1]) 7 | cost[i] = max(cost[i], cost[i - 1]) 8 | return res 9 | 10 | 11 | for s, cost in [ 12 | ('abaac', [1, 2, 3, 4, 5]), 13 | ('abc', [1, 2, 3]), 14 | ('aabaa', [1, 2, 3, 4, 1]) 15 | ]: 16 | print(main(s, cost)) 17 | -------------------------------------------------------------------------------- /greedy/leetcode/minimum/minimum-deletions-to-make-character-frequencies-unique-1647.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=1 5 | def main(s): 6 | res = 0 7 | vis = set() 8 | for count in Counter(s).values(): 9 | while count and count in vis: 10 | count -= 1 11 | res += 1 12 | vis.add(count) 13 | return res 14 | 15 | 16 | for s in [ 17 | 'aab', 18 | 'aaabbbcc', 19 | 'ceabaacb' 20 | ]: 21 | print(main(s)) 22 | -------------------------------------------------------------------------------- /greedy/leetcode/minimum/minimum-difference-between-largest-and-smallest-values-in-three-moves-1509.py: -------------------------------------------------------------------------------- 1 | # T=nlogn 2 | def main(nums): 3 | if len(nums) < 5: 4 | return 0 5 | nums = sorted(nums) 6 | return min(nums[i - 4] - nums[i] for i in range(4)) 7 | 8 | 9 | for nums in [ 10 | [1, 3], 11 | [1, 3, 8], 12 | [5, 3, 2, 4], 13 | [1, 5, 0, 10, 14], 14 | [6, 6, 0, 1, 1, 4, 6] 15 | ]: 16 | print(main(nums)) 17 | -------------------------------------------------------------------------------- /greedy/leetcode/minimum/minimum-number-of-arrows-to-burst-balloons-452.py: -------------------------------------------------------------------------------- 1 | # T=nlogn,S=n 2 | def main(nums): 3 | if not nums: 4 | return 0 5 | res = 1 6 | nums.sort(key=lambda x: x[1]) 7 | firstEnd = nums[0][1] 8 | for start, end in nums: 9 | if firstEnd < start: 10 | firstEnd = end 11 | res += 1 12 | return res 13 | 14 | 15 | for nums in [ 16 | [[10, 16], [2, 8], [1, 6], [7, 12]], 17 | [[1, 2], [3, 4], [5, 6], [7, 8]], 18 | [[1, 2], [2, 3], [3, 4], [4, 5]] 19 | ]: 20 | print(main(nums)) 21 | -------------------------------------------------------------------------------- /greedy/leetcode/popular/container-with-most-water-11.py: -------------------------------------------------------------------------------- 1 | inputs = [ 2 | [1, 8, 6, 2, 5, 4, 8, 3, 7], 3 | [4, 3, 2, 1, 4], 4 | [1, 2, 1], 5 | [1, 1] 6 | ] 7 | 8 | 9 | # T=n²,S=1 10 | def main(nums): 11 | n = len(nums) 12 | res = 0 13 | for left in range(n): 14 | for right in range(left + 1, n): 15 | area = min(nums[left], nums[right]) * (right - left) 16 | res = max(res, area) 17 | return res 18 | 19 | 20 | for nums in inputs: 21 | print(main(nums), end=' ') 22 | 23 | print() 24 | 25 | 26 | # T=n,S=1 27 | def main(nums): 28 | res = left = 0 29 | right = len(nums) - 1 30 | while left < right: 31 | area = min(nums[left], nums[right]) * (right - left) 32 | res = max(res, area) 33 | if nums[left] < nums[right]: 34 | left += 1 35 | else: 36 | right -= 1 37 | return res 38 | 39 | 40 | for nums in inputs: 41 | print(main(nums), end=' ') 42 | -------------------------------------------------------------------------------- /greedy/leetcode/popular/gas-station-134.py: -------------------------------------------------------------------------------- 1 | inputs = [ 2 | ([1, 2, 3, 4, 5], [3, 4, 5, 1, 2]), 3 | ([2, 3, 4], [3, 4, 3]) 4 | ] 5 | 6 | 7 | # T=n²,S=1 8 | def main(gas, cost): 9 | n = len(gas) 10 | for i in range(n): 11 | tank = 0 12 | for j in range(i, i + n): 13 | tank += (gas[j % n] - cost[j % n]) 14 | if tank < 0: 15 | break 16 | if tank >= 0: 17 | return i 18 | return -1 19 | 20 | 21 | for gas, cost in inputs: 22 | print(main(gas, cost), end=' ') 23 | 24 | print() 25 | 26 | 27 | # T=n,S=1 28 | def main(gas, cost): 29 | if sum(gas) < sum(cost): 30 | return -1 31 | tank = res = 0 32 | for i in range(len(gas)): 33 | tank += (gas[i] - cost[i]) 34 | if tank < 0: 35 | res = i + 1 36 | tank = 0 37 | return res 38 | 39 | 40 | for gas, cost in inputs: 41 | print(main(gas, cost), end=' ') 42 | -------------------------------------------------------------------------------- /greedy/leetcode/reorganize-string-767.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from heapq import * 3 | 4 | 5 | # T=nlogk,S=k 6 | def main(s): 7 | heap = [[-count, char] for char, count in Counter(s).items()] 8 | heapify(heap) 9 | res = [] 10 | prev = [0, ''] 11 | while heap: 12 | curr = heappop(heap) 13 | res.append(curr[1]) 14 | curr[0] += 1 15 | if prev[0] < 0: 16 | heappush(heap, prev) 17 | prev = curr 18 | return '' if len(res) != len(s) else ''.join(res) 19 | 20 | 21 | for s in [ 22 | 'aab', 23 | 'aaab', 24 | 'abbabbaaab' 25 | ]: 26 | print(main(s)) 27 | -------------------------------------------------------------------------------- /greedy/leetcode/time/car-pooling-1094.py: -------------------------------------------------------------------------------- 1 | # T=nlogn,S=n 2 | def main(trips, capacity): 3 | time = [] 4 | for change, start, end in trips: 5 | time.append((start, change)) 6 | time.append((end, -change)) 7 | time.sort() 8 | for _, change in time: 9 | capacity -= change 10 | if capacity < 0: 11 | return False 12 | return True 13 | 14 | 15 | for trips, capacity in [ 16 | ([[2, 1, 5], [3, 3, 7]], 4), 17 | ([[2, 1, 5], [3, 3, 7]], 5), 18 | ([[2, 1, 5], [3, 5, 7]], 3), 19 | ([[3, 2, 7], [3, 7, 9], [8, 3, 9]], 11), 20 | ]: 21 | print(main(trips, capacity)) 22 | -------------------------------------------------------------------------------- /greedy/leetcode/time/meeting-rooms-II-253.py: -------------------------------------------------------------------------------- 1 | # T=nlogn,S=n 2 | def main(nums): 3 | time = [] 4 | for start, end in nums: 5 | time.append((start, 1)) 6 | time.append((end, -1)) 7 | time.sort() 8 | res = count = 0 9 | for _, room in time: 10 | count += room 11 | res = max(res, count) 12 | return res 13 | 14 | 15 | for nums in [ 16 | [[0, 30], [5, 10], [15, 20]], 17 | [[7, 10], [2, 4]], 18 | [[1, 10], [2, 7], [3, 19], [8, 12], [10, 20], [11, 30]] 19 | ]: 20 | print(main(nums)) 21 | -------------------------------------------------------------------------------- /grokking/15.k-way-merge/k-smallest-elements-in-m-sorted-arrays.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | 3 | 4 | # T=m+klogm,S=m+k 5 | def main(arrays, k): 6 | res = [] 7 | heap = [] 8 | for i in range(len(arrays)): 9 | if arrays[i]: 10 | heap.append((arrays[i][0], i, 0, len(arrays[i]))) 11 | heapify(heap) 12 | for _ in range(k): 13 | val, ri, ci, size = heappop(heap) 14 | res.append(val) 15 | if ci + 1 < size: 16 | heappush(heap, (arrays[ri][ci + 1], ri, ci + 1, size)) 17 | return res 18 | 19 | 20 | for arrays, k in [ 21 | ([[2, 6, 8], [3, 6, 7], [1, 3, 4]], 5), 22 | ([[5, 8, 9], [1, 7]], 3), 23 | ]: 24 | print(main(arrays, k)) 25 | -------------------------------------------------------------------------------- /grokking/15.k-way-merge/kth-smallest-element-in-m-sorted-arrays.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | 3 | 4 | # T=m+klogm,S=m 5 | def main(arrays, k): 6 | res = None 7 | heap = [] 8 | for i in range(len(arrays)): 9 | if arrays[i]: 10 | heap.append((arrays[i][0], i, 0, len(arrays[i]))) 11 | heapify(heap) 12 | for _ in range(k): 13 | res, ri, ci, size = heappop(heap) 14 | if ci + 1 < size: 15 | heappush(heap, (arrays[ri][ci + 1], ri, ci + 1, size)) 16 | return res 17 | 18 | 19 | for arrays, k in [ 20 | ([[2, 6, 8], [3, 6, 7], [1, 3, 4]], 5), 21 | ([[5, 8, 9], [1, 7]], 3), 22 | ]: 23 | print(main(arrays, k)) 24 | -------------------------------------------------------------------------------- /grokking/15.k-way-merge/leetcode/smallest-range-covering-elements-from-k-lists-632.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | 3 | 4 | # T=k+nklogk,S=k 5 | def main(arrays): 6 | heap = [] 7 | start = maxVal = float('-inf') 8 | end = float('inf') 9 | for i in range(len(arrays)): 10 | heap.append((arrays[i][0], i, 0, len(arrays[i]))) 11 | maxVal = max(maxVal, arrays[i][0]) 12 | heapify(heap) 13 | while heap: 14 | minVal, ri, ci, size = heappop(heap) 15 | if maxVal - minVal < end - start: 16 | start, end = minVal, maxVal 17 | if ci + 1 == size: 18 | return [start, end] 19 | heappush(heap, (arrays[ri][ci + 1], ri, ci + 1, size)) 20 | maxVal = max(maxVal, arrays[ri][ci + 1]) 21 | 22 | 23 | for arrays in [ 24 | [[1, 5, 8], [4, 12], [7, 8, 10]], 25 | [[1, 9], [4, 12], [7, 10, 16]] 26 | ]: 27 | print(main(arrays)) 28 | -------------------------------------------------------------------------------- /hashmap/find-itinerary-from-tickets.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(mapping): 3 | start = None 4 | sources = mapping.keys() 5 | destinations = set(mapping.values()) 6 | for source in sources: 7 | if source not in destinations: 8 | start = source 9 | break 10 | while start in mapping: 11 | print(start, mapping[start]) 12 | start = mapping[start] 13 | 14 | 15 | for mapping in [ 16 | { 17 | 'Chennai': 'Bangalore', 18 | 'Bombay': 'Delhi', 19 | 'Goa': 'Chennai', 20 | 'Delhi': 'Goa' 21 | } 22 | ]: 23 | main(mapping) 24 | -------------------------------------------------------------------------------- /hashmap/highest-frequency-character.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | def main(s): 5 | res = s[0] 6 | freq = Counter(s) 7 | for ch in count: 8 | if count[ch] > count[res]: 9 | res = ch 10 | return res 11 | 12 | 13 | for s in ['babcccdbabcccd']: 14 | print(main(s)) 15 | -------------------------------------------------------------------------------- /hashmap/largest-subarray-with-contiguous-elements-(distinct elements).py: -------------------------------------------------------------------------------- 1 | # T=n²,S=1 2 | def main(nums): 3 | n = len(nums) 4 | res = 1 5 | for i in range(n): 6 | minVal = float('inf') 7 | maxVal = float('-inf') 8 | for j in range(i, n): 9 | minVal = min(minVal, nums[j]) 10 | maxVal = max(maxVal, nums[j]) 11 | if maxVal - minVal == j - i: 12 | res = max(res, j - i + 1) 13 | return res 14 | 15 | 16 | for nums in [ 17 | [1, 2, 3], 18 | [10, 12, 11], 19 | [14, 12, 11, 20], 20 | [1, 56, 58, 57, 90, 92, 94, 93, 91, 45] 21 | ]: 22 | print(main(nums)) 23 | -------------------------------------------------------------------------------- /hashmap/largest-subarray-with-contiguous-elements.py: -------------------------------------------------------------------------------- 1 | # T=n²,S=1 2 | def main(nums): 3 | n = len(nums) 4 | res = 0 5 | for i in range(n): 6 | minVal = float('inf') 7 | maxVal = float('-inf') 8 | vis = set() 9 | for j in range(i, n): 10 | if nums[j] in vis: 11 | break 12 | vis.add(nums[j]) 13 | minVal = min(minVal, nums[j]) 14 | maxVal = max(maxVal, nums[j]) 15 | if maxVal - minVal == j - i: 16 | res = max(res, j - i + 1) 17 | return res 18 | 19 | 20 | for nums in [ 21 | [], 22 | [5], 23 | [1, 2, 3], 24 | [10, 12, 11], 25 | [14, 12, 11, 20], 26 | [1, 56, 58, 57, 90, 92, 94, 93, 91, 45], 27 | [10, 12, 12, 10, 10, 11, 10], 28 | [9, 2, 7, 5, 6, 23, 24, 22, 23, 19, 17, 16, 18, 39, 0] 29 | ]: 30 | print(main(nums)) 31 | -------------------------------------------------------------------------------- /hashmap/leetcode/2-arrays/intersection-of-two-arrays-349.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=m+n,S=m+n 5 | def x(nums1, nums2): 6 | res = [] 7 | hm = Counter(nums1) 8 | for val in nums2: 9 | if val in hm: 10 | res.append(val) 11 | del hm[val] 12 | return res 13 | 14 | 15 | # T=m+n,S=m+n 16 | def y(nums1, nums2): 17 | res = [] 18 | nums1Set = set(nums1) 19 | for val in nums2: 20 | if val in nums1Set: 21 | res.append(val) 22 | nums1Set.remove(val) 23 | return res 24 | 25 | 26 | # T=m+n,S=m+n 27 | def z(nums1, nums2): 28 | return list(set(nums1) & set(nums2)) 29 | 30 | 31 | for nums1, nums2 in [ 32 | ([1, 1, 2, 2, 2, 3, 5], [1, 1, 1, 2, 2, 4, 5]), 33 | ([4, 9, 5], [9, 4, 9, 8, 4]), 34 | ]: 35 | print(x(nums1, nums2)) 36 | print(y(nums1, nums2)) 37 | print(z(nums1, nums2)) 38 | -------------------------------------------------------------------------------- /hashmap/leetcode/anagrams/find-anagram-mappings-760.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, deque 2 | 3 | 4 | # T=n,S=n 5 | def main(nums1, nums2): 6 | res = [] 7 | mapping = defaultdict(deque) 8 | for i, v in enumerate(nums2): 9 | mapping[v].append(i) 10 | return [mapping[val].popleft() for val in nums1] 11 | 12 | 13 | for nums1, nums2 in [ 14 | ([12, 28, 46, 32, 50], [50, 12, 32, 46, 28]), 15 | ([84, 46], [84, 46]), 16 | ([12, 28, 46], [46, 12, 28]), 17 | ([2, 7, 9, 2, 8, 1, 1, 3, 9], [3, 1, 2, 9, 8, 1, 7, 9, 2]) 18 | ]: 19 | print(main(nums1, nums2)) 20 | -------------------------------------------------------------------------------- /hashmap/leetcode/anagrams/group-anagrams-49.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | inputs = [ 4 | ['eat', 'tea', 'tan', 'ate', 'nat', 'bat'], 5 | ['pepcoding', 'codingpep', 'pepper', 'rapper', 'repepp'], 6 | ['a'], 7 | [''] 8 | ] 9 | 10 | 11 | # T=NKlogK,S=NK 12 | def main(words): 13 | group = defaultdict(list) 14 | for word in words: 15 | group[''.join(sorted(word))].append(word) 16 | return group.values() 17 | 18 | 19 | for words in inputs: 20 | print(main(words)) 21 | 22 | print() 23 | 24 | 25 | # T=NK,S=NK 26 | def main(words): 27 | group = defaultdict(list) 28 | for word in words: 29 | count = [0] * 26 30 | for ch in word: 31 | count[ord(ch) - ord('a')] += 1 32 | group[tuple(count)].append(word) 33 | return group.values() 34 | 35 | 36 | for words in inputs: 37 | print(main(words)) 38 | -------------------------------------------------------------------------------- /hashmap/leetcode/anagrams/valid-anagram-242.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | inputs = [ 4 | ('anagram', 'nagaram'), 5 | ('rat', 'car') 6 | ] 7 | 8 | 9 | # T=nlogn,S=n 10 | def main(s, t): 11 | if len(s) != len(t): 12 | return False 13 | return sorted(s) == sorted(t) 14 | 15 | 16 | for s, t in inputs: 17 | print(main(s, t)) 18 | 19 | 20 | # T=n,S=1 21 | def main(s, t): 22 | if len(s) != len(t): 23 | return False 24 | return Counter(s) == Counter(t) 25 | 26 | 27 | for s, t in inputs: 28 | print(main(s, t)) 29 | -------------------------------------------------------------------------------- /hashmap/leetcode/check-if-array-pairs-are-divisible-by-k-1497.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=n 5 | def main(nums, k): 6 | freq = Counter() 7 | for val in nums: 8 | freq[val % k] += 1 9 | if freq[0] % 2 == 1: 10 | return False 11 | del freq[0] 12 | for remainder in freq: 13 | if freq[remainder] != freq[k - remainder]: 14 | return False 15 | return True 16 | 17 | 18 | for nums, k in [ 19 | ([11, 17, 13, 18], 10), 20 | ([1, 2, 3, 4, 5, 10, 6, 7, 8, 9], 5), 21 | ([1, 2, 3, 4, 5, 6], 7), 22 | ([1, 2, 3, 4, 5, 6], 10), 23 | ([-10, 10], 2), 24 | ([-1, 1, -2, 2, -3, 3, -4, 4], 3), 25 | ([1, 1, 1, 1, 1, 1], 6), 26 | ]: 27 | print(main(nums, k)) 28 | -------------------------------------------------------------------------------- /hashmap/leetcode/fixed-window/count-distinct-elements-in-every-window-of-size-k.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=n 5 | def main(nums, k): 6 | n = len(nums) 7 | freq = Counter(nums[:k]) 8 | res = [len(freq)] 9 | for i in range(k, n): 10 | freq[nums[i - k]] -= 1 11 | if not freq[nums[i - k]]: 12 | del freq[nums[i - k]] 13 | freq[nums[i]] += 1 14 | res.append(len(freq)) 15 | return res 16 | 17 | 18 | for nums, k in [ 19 | ([1, 2, 1, 3, 4, 2, 3], 4), 20 | ([1, 2, 4, 4], 2), 21 | ([2, 5, 5, 6, 3, 2, 3, 2, 4, 5, 2, 2, 2, 2, 3, 6], 4) 22 | ]: 23 | print(main(nums, k)) 24 | -------------------------------------------------------------------------------- /hashmap/leetcode/fixed-window/permutation-in-string-567-(check-if-s2-contains-a-permutation-of-s1).py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=m+(n-m),S=m 5 | def main(s1, s2): 6 | m, n = len(s1), len(s2) 7 | if m > n: 8 | return False 9 | map1 = Counter(s1) 10 | map2 = Counter(s2[:m]) 11 | if map1 == map2: 12 | return True 13 | for i in range(m, n): 14 | map2[s2[i - m]] -= 1 15 | if not map2[s2[i - m]]: 16 | del map2[s2[i - m]] 17 | map2[s2[i]] += 1 18 | if map1 == map2: 19 | return True 20 | return False 21 | 22 | 23 | for s1, s2 in [ 24 | ('ab', 'eidbaooo'), 25 | ('ab', 'eidboaoo'), 26 | ('ab', 'a') 27 | ]: 28 | print(main(s1, s2)) 29 | -------------------------------------------------------------------------------- /hashmap/leetcode/max-points-on-a-line-149.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n²,S=n 5 | def main(points): 6 | n = len(points) 7 | res = 0 8 | for i in range(n): 9 | x1, y1 = points[i][0], points[i][1] 10 | slopeFreq = Counter() 11 | for j in range(i + 1, n): 12 | x2, y2 = points[j][0], points[j][1] 13 | num, den = y2 - y1, x2 - x1 14 | slope = num / den if den else float('inf') 15 | slopeFreq[slope] += 1 16 | res = max(res, 1 + max(slopeFreq.values(), default=0)) 17 | return res 18 | 19 | 20 | for points in [ 21 | [[1, 1], [2, 2], [3, 3]], 22 | [[1, 1], [3, 2], [5, 3], [4, 1], [2, 3], [1, 4]] 23 | ]: 24 | print(main(points)) 25 | -------------------------------------------------------------------------------- /hashmap/leetcode/remove-duplicates-from-a-list-of-lists.py: -------------------------------------------------------------------------------- 1 | def w(nums): 2 | res = [] 3 | nums = sorted(nums) 4 | res, nums = [], sorted(nums) 5 | for i in range(len(nums)): 6 | if i and nums[i] == nums[i - 1]: 7 | continue 8 | res.append(nums[i]) 9 | return res 10 | 11 | 12 | def x(nums): 13 | res = [] 14 | nums = sorted(nums) 15 | for i in range(len(nums)): 16 | if not i or nums[i] != nums[i - 1]: 17 | res.append(nums[i]) 18 | return res 19 | 20 | 21 | def y(nums): 22 | res = {tuple(val) for val in nums} 23 | return [list(val) for val in res] 24 | 25 | 26 | def z(nums): 27 | res = set(map(tuple, nums)) 28 | return list(map(list, res)) 29 | 30 | 31 | for nums in [ 32 | [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]] 33 | ]: 34 | print(w(nums)) 35 | print(x(nums)) 36 | print(y(nums)) 37 | print(z(nums)) 38 | -------------------------------------------------------------------------------- /hashmap/leetcode/subarray-sum/divisible/largest-subarray-with-sum-divisible-by-k-(length).py: -------------------------------------------------------------------------------- 1 | # T=n,S=k 2 | def main(nums, k): 3 | res = sum = 0 4 | remainderToIndex = {0: -1} 5 | for i in range(len(nums)): 6 | sum += nums[i] 7 | remainder = sum % k 8 | if remainder in remainderToIndex: 9 | res = max(res, i - remainderToIndex[remainder]) 10 | if remainder not in remainderToIndex: 11 | remainderToIndex[remainder] = i 12 | return res 13 | 14 | 15 | for nums, k in [ 16 | ([2, 3, 5, 4, 5, 3, 4], 7), 17 | ([2, 7, 6, 1, 4, 5], 3), 18 | ([2, -6, 3, 1, 2, 8, 2, 1], 7), 19 | ([4, 5, 0, -2, -3, 1], 5), 20 | ([-2, 2, -5, 12, -11, -1, 7], 3), 21 | ([5], 9) 22 | ]: 23 | print(main(nums, k)) 24 | -------------------------------------------------------------------------------- /hashmap/leetcode/subarray-sum/divisible/pair-of-songs-with-total-durations-divisible-by-60-1010.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=n 5 | def main(nums): 6 | res = 0 7 | remainders = [val % 60 for val in nums] 8 | remainderFreq = Counter(remainders) 9 | for remainder in remainders: 10 | complement = 60 - remainder 11 | res += remainderFreq[complement] if remainder else remainderFreq[0] 12 | if remainder == complement or not remainder: 13 | res -= 1 14 | return res // 2 15 | 16 | 17 | for nums in [ 18 | [30, 20, 150, 100, 40], 19 | [60, 60, 60, 60], 20 | [60, 60, 60] 21 | ]: 22 | print(main(nums)) 23 | -------------------------------------------------------------------------------- /hashmap/leetcode/subarray-sum/divisible/subarray-sums-divisible-by-k-974-(count).py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=k 5 | def main(nums, k): 6 | res = sum = 0 7 | remainderFreq = Counter({0: 1}) 8 | for i in range(len(nums)): 9 | sum += nums[i] 10 | res += remainderFreq[sum % k] 11 | remainderFreq[sum % k] += 1 12 | return res 13 | 14 | 15 | for nums, k in [ 16 | ([2, 3, 5, 4, 5, 3, 4], 7), 17 | ([2, 7, 6, 1, 4, 5], 3), 18 | ([2, -6, 3, 1, 2, 8, 2, 1], 7), 19 | ([4, 5, 0, -2, -3, 1], 5), 20 | ([5], 9) 21 | ]: 22 | print(main(nums, k)) 23 | -------------------------------------------------------------------------------- /hashmap/leetcode/subarray-sum/k/subarray-sum-equals-k-560-(count).py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n²,S=1 5 | def x(nums, k): 6 | n = len(nums) 7 | res = 0 8 | for i in range(n): 9 | sum = 0 10 | for j in range(i, n): 11 | sum += nums[j] 12 | if sum == k: 13 | res += 1 14 | return res 15 | 16 | 17 | # T=n,S=n 18 | def y(nums, k): 19 | res = sum = 0 20 | sumFreq = Counter({0: 1}) 21 | for i in range(len(nums)): 22 | sum += nums[i] 23 | res += sumFreq[sum - k] 24 | sumFreq[sum] += 1 25 | return res 26 | 27 | 28 | for nums, k in [ 29 | ([1, 1, 1], 2), 30 | ([1, 2, 3], 3), 31 | ([10, 5, 2, 7, 1, 9], 15), 32 | ([-5, 8, -14, 2, 4, 12], -5), 33 | ([3, 9, -2, 4, 1, -7, 2, 6, -5, 8, -3, -7, 6, 2, 1], 5) 34 | ]: 35 | print(x(nums, k), end=' ') 36 | print(y(nums, k)) 37 | -------------------------------------------------------------------------------- /hashmap/leetcode/subarray-sum/zero/contiguous-array/count-subarrays-with-an-equal-number-of-0-and-1.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=n 5 | def main(nums): 6 | res = sum = 0 7 | sumFreq = Counter({0: 1}) 8 | nums = [-1 if not val else 1 for val in nums] 9 | for i in range(len(nums)): 10 | sum += nums[i] 11 | res += sumFreq[sum] 12 | sumFreq[sum] += 1 13 | return res 14 | 15 | 16 | for nums in [ 17 | [0, 1], 18 | [0, 1, 0], 19 | [0, 1, 1, 0, 1, 1], 20 | [1, 1, 1, 1, 0, 0], 21 | [1, 0, 0, 1, 0, 1, 1], 22 | [0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1] 23 | ]: 24 | print(main(nums)) 25 | -------------------------------------------------------------------------------- /hashmap/leetcode/subdomain-visit-count-811.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=n 5 | def main(cpdomains): 6 | freq = Counter() 7 | for cpdomain in cpdomains: 8 | count, domain = cpdomain.split(' ') 9 | subdomains = domain.split('.') 10 | for i in range(len(subdomains)): 11 | freq['.'.join(subdomains[i:])] += int(count) 12 | return [str(count) + ' ' + subdomain for subdomain, count in freq.items()] 13 | 14 | 15 | for cpdomains in [ 16 | ['9001 discuss.leetcode.com'], 17 | ['900 google.mail.com', '50 yahoo.com', '1 intel.mail.com', '5 wiki.org'] 18 | ]: 19 | print(main(cpdomains)) 20 | -------------------------------------------------------------------------------- /hashmap/longest-consecutive-sequence.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(nums): 3 | numsSet = set(nums) 4 | maxSize = 0 5 | longestConsecutiveSeq = [] 6 | for val in nums: 7 | if val - 1 not in numsSet: 8 | size = 1 9 | seq = [val] 10 | while val + 1 in numsSet: 11 | val += 1 12 | size += 1 13 | seq.append(val) 14 | if size > maxSize: 15 | maxSize = size 16 | longestConsecutiveSeq = seq 17 | print(longestConsecutiveSeq, maxSize) 18 | 19 | 20 | for nums in [ 21 | [100, 4, 200, 1, 3, 2], 22 | [0, 3, 7, 2, 5, 8, 4, 6, 0, 1], 23 | [10, 5, 9, 1, 11, 8, 6, 15, 3, 12, 2] 24 | ]: 25 | main(nums) 26 | -------------------------------------------------------------------------------- /hashmap/practice/counter.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | nums = [1, 2, 3, 4, 4, 5, 5, 5, 5, 7, 1, 1, 2, 4, 7, 8, 9, 6, 6, 6] 4 | hm = Counter(nums) 5 | print(hm) 6 | print(hm[100]) 7 | print(hm) 8 | print(hm.most_common(3)) 9 | 10 | print(sorted(hm.keys(), key=hm.get, reverse=True)) 11 | print(sorted(hm.keys(), key=lambda x: -hm[x])) 12 | 13 | s = 'loveleetcode' 14 | 15 | hm = Counter(s) 16 | print(''.join(sorted(s, key=lambda x: (-hm[x], x)))) 17 | print(''.join(ch * f for ch, f in Counter(s).most_common())) 18 | 19 | x = (ch * f for ch, f in Counter(s).most_common()) 20 | print(''.join(list(x))) 21 | 22 | print(Counter(s).most_common()) 23 | 24 | c = Counter(nums=3, b=1) 25 | d = Counter(nums=1, b=2) 26 | print(c, d) 27 | print(c + d) 28 | print(c - d) 29 | print(d - c) 30 | print(c & d) 31 | print(c | d) 32 | 33 | print((c & d).elements()) 34 | -------------------------------------------------------------------------------- /hashmap/practice/hashmap-using-list.py: -------------------------------------------------------------------------------- 1 | hm = [[] for _ in range(10)] 2 | 3 | 4 | def put(key, val): 5 | bucket = hcode(key) 6 | for i, (k, _) in enumerate(hm[bucket]): 7 | if key == k: 8 | hm[bucket][i] = (key, val) 9 | return 10 | hm[bucket].append((key, val)) 11 | 12 | 13 | def get(key): 14 | for k, v in hm[hcode(key)]: 15 | if key == k: 16 | return v 17 | 18 | 19 | def remove(key): 20 | bucket = hcode(key) 21 | for i, (k, _) in enumerate(hm[bucket]): 22 | if key == k: 23 | del hm[bucket][i] 24 | return 25 | 26 | 27 | def hcode(key): 28 | return hash(key) % 10 29 | 30 | 31 | put(10, 'Nepal') 32 | put(25, 'USA') 33 | put(26, 'India') 34 | put(20, 'Iran') 35 | put(20, 'Israel') 36 | print(hm) 37 | remove(20) 38 | print(hm) 39 | -------------------------------------------------------------------------------- /hashmap/practice/hashmap.py: -------------------------------------------------------------------------------- 1 | hm = {'India': 135, 'China': 200, 'Pakistan': 30, 'US': 20, 'UK': 10} 2 | print(hm) 3 | print(hm.keys()) 4 | print(hm.values()) 5 | print(hm.items()) 6 | 7 | print(hm.pop('India')) 8 | print(hm) 9 | del hm['China'] 10 | print(hm) 11 | print(hm.popitem()) 12 | print(hm) 13 | 14 | print(dict.fromkeys([1, 2, 3, 4], 10)) 15 | 16 | hm.update(India=135, China=200) 17 | print(hm) 18 | hm.update([('x', 2), ('z', 3)]) 19 | print(hm) 20 | hm['India'] = 140 21 | print(hm) 22 | 23 | print(hm.get('India')) 24 | print(hm.get('Iran')) 25 | 26 | print('Iraq' in hm) 27 | print('Iran' in hm) 28 | print('India' in hm) 29 | 30 | for k, v in hm.items(): 31 | print(k, v) 32 | 33 | print(hash(10) % 10) 34 | print(hash(30) % 10) 35 | print(hash(31) % 10) 36 | print(hash(5) % 10) 37 | print(hash(15) % 10) 38 | 39 | x = [1, 2, 3] 40 | del x[1] 41 | print(x) 42 | -------------------------------------------------------------------------------- /hashmap/practice/map-and-filter.py: -------------------------------------------------------------------------------- 1 | nums = [5, 7, 22, 97, 54, 62, 77, 23, 73, 61] 2 | nums = filter(lambda x: x % 2 == 0, nums) 3 | print(nums) 4 | print(next(nums)) 5 | print(list(nums)) 6 | 7 | nums = map(lambda x: x % 2 == 0, nums) 8 | print(list(nums)) 9 | 10 | nums = map(lambda x: x // 2, nums) 11 | print(list(nums)) 12 | 13 | p = map(lambda x, y: x + y, (1, 2, 3), (4, 5, 6)) 14 | print(p) 15 | print(list(p)) 16 | 17 | animals = ['dog', 'cat', 'parrot', 'rabbit'] 18 | animals = list(map(lambda animal: animal.upper(), animals)) 19 | print(animals) 20 | -------------------------------------------------------------------------------- /hashmap/subarrays.py: -------------------------------------------------------------------------------- 1 | # T=n³ 2 | def main(nums): 3 | n = len(nums) 4 | res = [] 5 | for i in range(n): 6 | for j in range(i, n): 7 | res.append(nums[i:j + 1]) 8 | return res 9 | 10 | 11 | for nums in [ 12 | [], [1], [1, 2, 3] 13 | ]: 14 | print(main(nums)) 15 | 16 | print() 17 | 18 | 19 | # T=n³ 20 | def main(s): 21 | n = len(s) 22 | res = [] 23 | for i in range(n): 24 | for j in range(i, n): 25 | res.append(s[i:j + 1]) 26 | return res 27 | 28 | 29 | for s in [ 30 | '', 'a', 'abc' 31 | ]: 32 | print(main(s)) 33 | -------------------------------------------------------------------------------- /heap/heap-operations.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | 3 | nums = [9, 4, 5, 6, 1] 4 | heapify(nums) 5 | print(heappop(nums)) 6 | print(nlargest(3, nums)) 7 | print(nsmallest(3, nums)) 8 | 9 | nums = [9, 4, 5] 10 | heapify(nums) 11 | print(nums[0]) 12 | heappushpop(nums, 13) 13 | print(nums[0], nums) 14 | 15 | heap = [] 16 | for val in [9, 4, 5, 6, 1]: 17 | heappush(heap, -val) 18 | while heap: 19 | print(-heappop(heap), end=' ') 20 | 21 | print() 22 | 23 | nums, b, c = [1, 2, 3], [5, 7, 9], [6, 8, 10] 24 | print(list(merge(nums, b, c))) 25 | -------------------------------------------------------------------------------- /heap/heap-sort.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | 3 | 4 | def main(nums): 5 | heap = [] 6 | for val in nums: 7 | heappush(heap, val) 8 | for _ in range(len(nums)): 9 | print(heappop(heap), end=' ') 10 | 11 | 12 | for nums in [ 13 | [10, 2, 80, 9, 33], 14 | [17, 33, 27, 14, 18, 19, 21, 11, 9, 5], 15 | [8, 3, 6, 5, 9, 1] 16 | ]: 17 | main(nums) 18 | print() 19 | -------------------------------------------------------------------------------- /heap/implementation/build.py: -------------------------------------------------------------------------------- 1 | # T=n 2 | def heapify(nums): 3 | lastParentIndex = (len(nums) // 2) - 1 4 | for i in range(lastParentIndex, -1, -1): 5 | _downheapify(i) 6 | 7 | 8 | def _downheapify(index): 9 | n = len(nums) 10 | minIndex = index 11 | leftChildIndex = 2 * index + 1 12 | if leftChildIndex < n and nums[leftChildIndex] < nums[minIndex]: 13 | minIndex = leftChildIndex 14 | rightChildIndex = 2 * index + 2 15 | if rightChildIndex < n and nums[rightChildIndex] < nums[minIndex]: 16 | minIndex = rightChildIndex 17 | if minIndex != index: 18 | _swap(index, minIndex) 19 | _downheapify(minIndex) 20 | 21 | 22 | def _swap(i, j): 23 | nums[i], nums[j] = nums[j], nums[i] 24 | 25 | 26 | for nums in [ 27 | [13, 3, 10, 4, 8, 5, 6, 9, 15, 17], 28 | [5, 4, 1, 2, 3] 29 | ]: 30 | print(nums) 31 | heapify(nums) 32 | print(nums) 33 | print(nums[0]) 34 | -------------------------------------------------------------------------------- /heap/leetcode/k-closest-points-to-origin-973.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | 3 | 4 | # T=nlogn 5 | def x(points, k): 6 | return sorted(points, key=lambda x: x[0] ** 2 + x[1] ** 2)[:k] 7 | 8 | 9 | # T=nlogk,S=k 10 | def y(points, k): 11 | return nsmallest(k, points, key=lambda x: x[0] ** 2 + x[1] ** 2) 12 | 13 | 14 | # T=nlogk,S=k 15 | def z(points, k): 16 | heap = [] 17 | for x, y in points: 18 | d = x ** 2 + y ** 2 19 | heappush(heap, (-d, [x, y])) 20 | if len(heap) > k: 21 | heappop(heap) 22 | return [point for _, point in heap] 23 | 24 | 25 | for points, k in [ 26 | ([[1, 3], [-2, 2]], 1), 27 | ([[3, 3], [5, -1], [-2, 4]], 2) 28 | ]: 29 | print(x(points, k)) 30 | print(y(points, k)) 31 | print(z(points, k)) 32 | -------------------------------------------------------------------------------- /heap/leetcode/k-frequent/top-k-frequent-elements-347.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | from collections import Counter 3 | 4 | 5 | # T=nlogn,S=n 6 | def x(nums, k): 7 | freq = Counter(nums) 8 | return sorted(freq.keys(), key=freq.get, reverse=True)[:k] 9 | 10 | 11 | # T=nlogk,S=k 12 | def y(nums, k): 13 | freq = Counter(nums) 14 | return nlargest(k, freq.keys(), key=freq.get) 15 | 16 | 17 | # T=nlogk,S=n+k 18 | def z(nums, k): 19 | heap = [] 20 | for val, count in Counter(nums).items(): 21 | heappush(heap, (count, val)) 22 | if len(heap) > k: 23 | heappop(heap) 24 | while heap: 25 | res.append(heappop(heap)[1]) 26 | return res 27 | 28 | 29 | for nums, k in [ 30 | ([1, 1, 1, 2, 2, 3], 2), 31 | ([7, 10, 11, 5, 2, 5, 5, 7, 11, 8, 9], 4) 32 | ]: 33 | print(x(nums, k)) 34 | print(y(nums, k)) 35 | print(z(nums, k)) 36 | -------------------------------------------------------------------------------- /heap/leetcode/k-frequent/top-k-frequent-words-692.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | from collections import Counter 3 | 4 | 5 | def x(words, k): 6 | freq = Counter(words) 7 | return nsmallest(k, freq.keys(), lambda x: (-freq[x], x)) 8 | 9 | 10 | # T=nlogk,S=n 11 | def y(words, k): 12 | heap = [] 13 | for word, count in Counter(words).items(): 14 | heappush(heap, (count, word)) 15 | if len(heap) > k: 16 | heappop(heap) 17 | return [word for _, word in heap] 18 | 19 | 20 | for words, k in [ 21 | (['i', 'love', 'leetcode', 'i', 'love', 'coding'], 2), 22 | ]: 23 | print(x(words, k)) 24 | print(y(words, k)) 25 | -------------------------------------------------------------------------------- /heap/leetcode/kth-smallest-and-largest/kth-largest-element-in-a-stream-703.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | 3 | 4 | # T=nlogk+mlogk,S=k 5 | class KthLargest: 6 | 7 | def __init__(self, k, nums): 8 | self.k = k 9 | self.heap = [] 10 | for val in nums: 11 | self.add(val) 12 | 13 | def add(self, val): 14 | heappush(self.heap, val) 15 | if len(self.heap) > self.k: 16 | heappop(self.heap) 17 | return self.heap[0] 18 | 19 | 20 | kthLargest = KthLargest(3, [4, 5, 8, 2]) 21 | print(kthLargest.add(3)) 22 | print(kthLargest.add(5)) 23 | print(kthLargest.add(10)) 24 | print(kthLargest.add(9)) 25 | print(kthLargest.add(4)) 26 | -------------------------------------------------------------------------------- /heap/leetcode/least-number-of-unique-integers-after-k-removals-1481.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from heapq import heappush, heappop 3 | 4 | inputs = [ 5 | ([5, 5, 4], 1), 6 | ([4, 3, 1, 1, 3, 3, 2], 3) 7 | ] 8 | 9 | 10 | # T=nlogn,S=n 11 | def main(nums, k): 12 | heap = [] 13 | for count in Counter(nums).values(): 14 | heappush(heap, count) 15 | while k: 16 | count = heappop(heap) 17 | if count > 1: 18 | heappush(heap, count - 1) 19 | k -= 1 20 | return len(heap) 21 | 22 | 23 | for nums, k in inputs: 24 | print(main(nums, k)) 25 | -------------------------------------------------------------------------------- /heap/leetcode/maximum-number-of-events-that-can-be-attended-1353.py: -------------------------------------------------------------------------------- 1 | from heapq import heappush, heappop 2 | 3 | 4 | # T=nlogn,S=n 5 | def main(events): 6 | n = len(events) 7 | events = sorted(events) 8 | i = res = day = 0 9 | heap = [] 10 | while i < n or heap: 11 | if not heap: 12 | day = events[i][0] 13 | while i < n and day == events[i][0]: 14 | heappush(heap, events[i][1]) 15 | i += 1 16 | heappop(heap) 17 | res += 1 18 | day += 1 19 | while heap and day > heap[0]: 20 | heappop(heap) 21 | return res 22 | 23 | 24 | for events in [ 25 | [[1, 2], [2, 3], [3, 4]], 26 | [[1, 2], [2, 3], [3, 4], [1, 2]], 27 | [[1, 4], [4, 4], [2, 2], [3, 4], [1, 1]], 28 | [[1, 100000]], 29 | [[1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7]] 30 | ]: 31 | print(main(events)) 32 | -------------------------------------------------------------------------------- /heap/leetcode/median-from-data-stream-295.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | 3 | minHeap = [] 4 | maxHeap = [] 5 | 6 | 7 | def add(val): 8 | heappush(maxHeap, -val) 9 | heappush(minHeap, -heappop(maxHeap)) 10 | if len(maxHeap) < len(minHeap): 11 | heappush(maxHeap, -heappop(minHeap)) 12 | 13 | 14 | def peek(): 15 | if len(maxHeap) > len(minHeap): 16 | return -maxHeap[0] 17 | return (minHeap[0] + -maxHeap[0]) / 2 18 | 19 | 20 | def main(nums): 21 | global minHeap, maxHeap 22 | minHeap = [] 23 | maxHeap = [] 24 | for val in nums: 25 | add(val) 26 | print(peek(), end=' ') 27 | 28 | 29 | for nums in [ 30 | [10, 20, 30, 40], 31 | [41, 35, 62, 5, 97, 108] 32 | ]: 33 | main(nums) 34 | print() 35 | -------------------------------------------------------------------------------- /heap/leetcode/minimum/minimum-number-of-refueling-stops-871.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | 3 | inputs = [ 4 | (100, 10, [[10, 60], [20, 30], [30, 30], [60, 40]]), 5 | (100, 1, [[10, 100]]), 6 | (1, 1, []) 7 | ] 8 | 9 | 10 | # T=nlogn,S=n 11 | def main(target, fuel, stations): 12 | heap = [] 13 | res = i = 0 14 | while fuel < target: 15 | while i < len(stations) and stations[i][0] <= fuel: 16 | heappush(heap, -stations[i][1]) 17 | i += 1 18 | if not heap: 19 | return -1 20 | fuel += -heappop(heap) 21 | res += 1 22 | return res 23 | 24 | 25 | for target, startFuel, stations in inputs: 26 | print(main(target, startFuel, stations)) 27 | -------------------------------------------------------------------------------- /heap/leetcode/util.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, val): 3 | self.val = val 4 | self.next = None 5 | 6 | def __str__(self): 7 | temp = self 8 | nums = [] 9 | while temp: 10 | nums.append(temp.val) 11 | temp = temp.next 12 | return str(nums) 13 | 14 | 15 | def add(head, val): 16 | if not head: 17 | return ListNode(val) 18 | temp = head 19 | while temp.next: 20 | temp = temp.next 21 | temp.next = ListNode(val) 22 | return head 23 | 24 | 25 | def build(nums): 26 | head = None 27 | for val in nums: 28 | head = add(head, val) 29 | return head if head else [] -------------------------------------------------------------------------------- /heap/median/median.py: -------------------------------------------------------------------------------- 1 | from statistics import median 2 | 3 | 4 | def x(nums): 5 | nums.sort() 6 | n = len(nums) 7 | i = n // 2 8 | if n % 2 == 1: 9 | return nums[i] 10 | return (nums[i - 1] + nums[i]) / 2 11 | 12 | 13 | def y(nums): 14 | return median(nums) 15 | 16 | 17 | for nums in [ 18 | [1], 19 | [1, 2], 20 | [1, 3, 4, 2, 6, 5, 8, 7], 21 | [4, 4, 4, 4, 4], 22 | [2, 4, 5, 6, 7, 8, 9, ] 23 | ]: 24 | print(x(nums), end=' ') 25 | print(y(nums), end=' ') 26 | print() 27 | -------------------------------------------------------------------------------- /heap/sort-k-sorted-array.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | 3 | 4 | def main(nums, k): 5 | n = len(nums) 6 | heap = [] 7 | for i in range(n): 8 | if i < k + 1: 9 | heappush(heap, nums[i]) 10 | else: 11 | print(heappop(heap), end=' ') 12 | heappush(heap, nums[i]) 13 | while heap: 14 | print(heappop(heap), end=' ') 15 | 16 | 17 | for nums, k in [ 18 | ([2, 3, 1, 4, 6, 7, 5, 8, 9], 2), 19 | ([6, 5, 3, 2, 8, 10, 9], 3), 20 | ([10, 9, 8, 7, 4, 70, 60, 50], 4), 21 | ([1, 4, 5, 2, 3, 7, 8, 6, 10, 9], 2) 22 | ]: 23 | main(nums, k) 24 | print() 25 | -------------------------------------------------------------------------------- /library/binarytree/bst.py: -------------------------------------------------------------------------------- 1 | from binarytree import bst 2 | 3 | print(bst()) 4 | print(bst(is_perfect=True)) 5 | -------------------------------------------------------------------------------- /library/binarytree/heap.py: -------------------------------------------------------------------------------- 1 | from binarytree import heap 2 | 3 | print(heap()) 4 | print(heap(is_max=False)) 5 | print(heap(is_perfect=True)) 6 | print(heap(is_max=False, is_perfect=True)) 7 | -------------------------------------------------------------------------------- /library/binarytree/tree.py: -------------------------------------------------------------------------------- 1 | from binarytree import tree, Node, build 2 | 3 | print(tree()) 4 | print(tree(is_perfect=True)) 5 | 6 | root = Node(1) 7 | root.left = Node(2) 8 | root.right = Node(3) 9 | root.left.left = Node(4) 10 | root.left.right = Node(5) 11 | print(root) 12 | 13 | print(root.properties) 14 | print(root.leaves) 15 | print(root.levels) 16 | 17 | original = tree() 18 | clone = original.clone() 19 | print(original.equals(clone)) 20 | 21 | print(root.inorder) 22 | print(root.preorder) 23 | print(root.postorder) 24 | 25 | print(root.values) 26 | 27 | print(list(root)) 28 | print(root.levelorder) 29 | 30 | root = build([7, 3, 2, 6, 9, None, 1, 5, 8]) 31 | print(root) 32 | print(root.values) 33 | 34 | root = build([7, 3, 2, 6, 9, 1, 5, 8]) 35 | print(root) 36 | -------------------------------------------------------------------------------- /library/bisect-module.py: -------------------------------------------------------------------------------- 1 | import bisect 2 | 3 | nums = [1, 2, 3, 3, 4, 4, 5] 4 | 5 | print(bisect.bisect(nums, 4)) 6 | print(bisect.bisect(nums, 5)) 7 | print(bisect.bisect(nums, 6)) 8 | 9 | print() 10 | print(bisect.bisect_right(nums, 4)) 11 | print(bisect.bisect_right(nums, 5)) 12 | print(bisect.bisect_right(nums, 6)) 13 | 14 | print() 15 | print(bisect.bisect_left(nums, 4)) 16 | print(bisect.bisect_left(nums, 5)) 17 | print(bisect.bisect_left(nums, 6)) 18 | -------------------------------------------------------------------------------- /library/dataclasses/explore.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, asdict, astuple 2 | 3 | 4 | @dataclass 5 | class Article: 6 | title: str 7 | author: str 8 | language: str 9 | upvotes: int 10 | 11 | 12 | article = Article('DataClasses', 'nikhil', 'Python', 0) 13 | print(article) 14 | print(asdict(article)) 15 | print(astuple(article)) 16 | 17 | 18 | @dataclass 19 | class CircleArea: 20 | r: int 21 | pi: float = 3.14 22 | 23 | @property 24 | def area(self): 25 | return self.pi * (self.r ** 2) 26 | 27 | 28 | nums = CircleArea(2) 29 | print(nums.area) 30 | -------------------------------------------------------------------------------- /library/sortedcontainers/explore.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import SortedDict, SortedList, SortedSet, SortedKeyList 2 | from operator import neg 3 | 4 | sl = SortedList([10, 5, 8, 9, 42]) 5 | sl.add(10) 6 | 7 | s2 = SortedList() 8 | for val in [3, 6, 1, 3, 8, -2]: 9 | s2.add(val) 10 | 11 | print(s2) 12 | 13 | s2 = SortedSet() 14 | for val in [3, 6, 1, 3, 8, -2]: 15 | s2.add(val) 16 | 17 | print(s2) 18 | 19 | sd = SortedDict({'c': 3, 'a': 1, 'b': 2}) 20 | print(sd) 21 | 22 | print(sd.peekitem()) 23 | 24 | ss = SortedSet('abracadabra') 25 | print(ss) 26 | 27 | skl = SortedKeyList([1, 2, 3, 4, 5], key=neg) 28 | print(skl) 29 | 30 | sd = SortedDict() 31 | sd['e'] = 5 32 | sd['b'] = 2 33 | print(sd) 34 | 35 | sd = SortedDict(neg, enumerate('abc', start=1)) 36 | print(sd) 37 | 38 | ss = SortedSet() 39 | ss.add('c') 40 | ss.add('b') 41 | ss.add('a') 42 | 43 | print(ss) 44 | -------------------------------------------------------------------------------- /linked/explore.py: -------------------------------------------------------------------------------- 1 | nums = 1 2 | b = 2 3 | c = 3 4 | 5 | c, e, f = 8, b, c + 1 6 | 7 | print(c, e, f) 8 | 9 | 10 | -------------------------------------------------------------------------------- /linked/leetcode/middle-of-the-linked-list-876.py: -------------------------------------------------------------------------------- 1 | from linked.util import build, ListNode 2 | 3 | inputs = [ 4 | build([1, 2, 3, 4, 5]), 5 | build([1, 2, 3, 4, 5, 6]) 6 | ] 7 | 8 | 9 | # T=n,S=1 10 | def main(head): 11 | count = 0 12 | temp = middle = head 13 | while temp: 14 | if count % 2: 15 | middle = middle.next 16 | count += 1 17 | temp = temp.next 18 | return middle 19 | 20 | 21 | for head in inputs: 22 | print(main(head)) 23 | 24 | print() 25 | 26 | 27 | # T=n,S=1 28 | def main(head): 29 | slow = fast = head 30 | while fast and fast.next: 31 | slow = slow.next 32 | fast = fast.next.next 33 | return slow 34 | 35 | 36 | for head in inputs: 37 | print(main(head)) 38 | -------------------------------------------------------------------------------- /linked/leetcode/odd-even-linked-list-328.py: -------------------------------------------------------------------------------- 1 | from linked.util import build, ListNode 2 | 3 | 4 | # T=n,S=1 5 | def main(head): 6 | if not head: 7 | return None 8 | oddHead = oddTail = head 9 | evenHead = evenTail = head.next 10 | while oddTail.next and evenTail.next: 11 | oddTail.next = oddTail.next.next 12 | oddTail = oddTail.next 13 | evenTail.next = evenTail.next.next 14 | evenTail = evenTail.next 15 | oddTail.next = evenHead 16 | return oddHead 17 | 18 | 19 | for head in [ 20 | build([1, 2, 3, 4, 5]), 21 | build([2, 1, 3, 5, 6, 4, 7]), 22 | build([1, 2, 3, 4]), 23 | build([1, 2, 3, 4, 5, 6, 7, 8]), 24 | build([]), 25 | build([1, 2]), 26 | build([1, 2, 3]) 27 | ]: 28 | print(main(head)) 29 | -------------------------------------------------------------------------------- /linked/leetcode/reverse-linked-list-206.py: -------------------------------------------------------------------------------- 1 | from linked.util import build 2 | 3 | 4 | # T=n,S=1 5 | def main(head): 6 | prev = None 7 | curr = head 8 | while curr: 9 | future = curr.next 10 | curr.next = prev 11 | prev = curr 12 | curr = future 13 | return prev 14 | 15 | 16 | for head in [ 17 | build([1]), 18 | build([1, 2, 3, 4, 5]), 19 | build([1, 2]) 20 | ]: 21 | print(main(head)) 22 | 23 | print() 24 | 25 | 26 | # T=n,S=n 27 | def main(head): 28 | def dfs(head): 29 | if not head or not head.next: 30 | return head 31 | last = dfs(head.next) 32 | head.next.next = head 33 | head.next = None 34 | return last 35 | 36 | return dfs(head) 37 | 38 | 39 | for head in [ 40 | build([1]), 41 | build([1, 2]), 42 | build([1, 2, 3, 4, 5]), 43 | ]: 44 | print(main(head)) 45 | -------------------------------------------------------------------------------- /linked/leetcode/rotate-list-61.py: -------------------------------------------------------------------------------- 1 | from linked.util import build, ListNode 2 | 3 | 4 | # T=n,S=1 5 | def main(head, k): 6 | if not head: 7 | return head 8 | tail = head 9 | n = 1 10 | while tail.next: 11 | n += 1 12 | tail = tail.next 13 | tail.next = head 14 | k %= n 15 | for i in range(n - k): 16 | tail = tail.next 17 | res = tail.next 18 | tail.next = None 19 | return res 20 | 21 | 22 | for head, k in [ 23 | (build([1, 2, 3, 4, 5]), 2), 24 | (build([0, 1, 2]), 4), 25 | (build([1]), 3), 26 | (build([]), 0) 27 | ]: 28 | print(main(head, k), end=' ') 29 | -------------------------------------------------------------------------------- /linked/leetcode/split-linked-list-in-parts-725.py: -------------------------------------------------------------------------------- 1 | from linked.util import build 2 | 3 | 4 | def size(head): 5 | size = 0 6 | while head: 7 | size += 1 8 | head = head.next 9 | return size 10 | 11 | 12 | # T=n+k,S=k 13 | def main(head, k): 14 | res = [] 15 | width, rem = divmod(size(head), k) 16 | i = 0 17 | prev = None 18 | curr = head 19 | while i < k and curr: 20 | res.append(curr) 21 | for _ in range(width - (0 if i < rem else 1)): 22 | curr = curr.next 23 | prev = curr 24 | curr = curr.next 25 | prev.next = None 26 | i += 1 27 | return res if i == k else res + (k - i) * [None] 28 | 29 | 30 | for head, k in [ 31 | (build([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 3), 32 | (build([1, 2, 3, 4, 5]), 5), 33 | (build([1, 2, 3, 4]), 5), 34 | (build([1, 2, 3]), 5), 35 | ]: 36 | print(main(head, k)) 37 | -------------------------------------------------------------------------------- /linked/util.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, val): 3 | self.val = val 4 | self.next = None 5 | 6 | def __str__(self): 7 | temp = self 8 | nums = [] 9 | while temp: 10 | nums.append(temp.val) 11 | temp = temp.next 12 | return str(nums) 13 | 14 | 15 | def add(head, val): 16 | if not head: 17 | return ListNode(val) 18 | temp = head 19 | while temp.next: 20 | temp = temp.next 21 | temp.next = ListNode(val) 22 | return head 23 | 24 | 25 | def build(nums): 26 | head = None 27 | for val in nums: 28 | head = add(head, val) 29 | return head if head else [] 30 | -------------------------------------------------------------------------------- /recursion/combinations-tree/all-root-to-node-paths.py: -------------------------------------------------------------------------------- 1 | def main(nums): 2 | n = len(nums) 3 | 4 | def dfs(start, path): 5 | print(path, end=' ') 6 | for i in range(start, n): 7 | dfs(i + 1, path + [nums[i]]) 8 | 9 | dfs(0, []) 10 | 11 | 12 | for nums in [ 13 | [1, 2, 3], 14 | [1, 2, 3, 4] 15 | ]: 16 | main(nums) 17 | print() 18 | -------------------------------------------------------------------------------- /recursion/combinations-tree/preorder.py: -------------------------------------------------------------------------------- 1 | def main(nums): 2 | n = len(nums) 3 | 4 | def dfs(start, prev): 5 | print(nums[prev] if prev != -1 else None, end=' ') 6 | for i in range(start, n): 7 | dfs(i + 1, i) 8 | 9 | dfs(0, -1) 10 | 11 | 12 | for nums in [ 13 | [1, 2, 3], 14 | [1, 2, 3, 4] 15 | ]: 16 | main(nums) 17 | print() 18 | -------------------------------------------------------------------------------- /recursion/combinations-tree/root-to-node-paths-of-size-k.py: -------------------------------------------------------------------------------- 1 | def main(nums, k): 2 | n = len(nums) 3 | 4 | def dfs(start, size): 5 | if size == k: 6 | return [[]] 7 | res = [] 8 | for i in range(start, n): 9 | for path in dfs(i + 1, size + 1): 10 | res.append([nums[i]] + path) 11 | return res 12 | 13 | return dfs(0, 0) 14 | 15 | 16 | for nums, k in [ 17 | ([1, 2, 3], 2), 18 | ([1, 2, 3, 4], 2) 19 | ]: 20 | print(main(nums, k)) 21 | -------------------------------------------------------------------------------- /recursion/explore.py: -------------------------------------------------------------------------------- 1 | print(str(1)) 2 | 3 | print(str(range(1, 10))) 4 | 5 | print('a' * (4 // 2)) 6 | 7 | print(['a']) 8 | 9 | map1 = {'a': 'xyz'} 10 | 11 | print(map1.get('b')) 12 | 13 | a = 'abcd' 14 | b = 'abc' 15 | 16 | print(set(a)) 17 | 18 | print(list(range(1, 4))) 19 | 20 | vis = set() 21 | vis.update({(1, 2), (3, 4), (5, 6)}) 22 | print(vis) 23 | 24 | vis.difference_update({(1, 2), (3, 4), (5, 6)}) 25 | print(vis) 26 | 27 | print(type([1, 2, 3]) == list) 28 | print(type([1, 2, 3]) is list) 29 | 30 | a = iter([1, 4, 7]) 31 | print(next(a)) 32 | print(next(a)) 33 | print(next(a)) 34 | 35 | s = 'abcd' 36 | for i in range(len(s)): 37 | prefix, suffix = s[:i + 1], s[i + 1:] 38 | print(prefix, suffix) 39 | 40 | print(''.join([])) 41 | -------------------------------------------------------------------------------- /recursion/grid/maze-paths-with-jumps.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikhilgoyal104/dsa/78b655dd8a9ba1b064ed0589cf6c35052c0560c6/recursion/grid/maze-paths-with-jumps.py -------------------------------------------------------------------------------- /recursion/grid/n-queens/print-all-configs.py: -------------------------------------------------------------------------------- 1 | def main(n): 2 | grid = [[0] * n for _ in range(n)] 3 | cols, diags, antidiags = set(), set(), set() 4 | 5 | def dfs(ri): 6 | if ri == n: 7 | print(grid) 8 | return 9 | for ci in range(n): 10 | if ci in cols or ri - ci in diags or ri + ci in antidiags: 11 | continue 12 | cols.add(ci) 13 | diags.add(ri - ci) 14 | antidiags.add(ri + ci) 15 | grid[ri][ci] = 1 16 | dfs(ri + 1) 17 | cols.remove(ci) 18 | diags.remove(ri - ci) 19 | antidiags.remove(ri + ci) 20 | grid[ri][ci] = 0 21 | 22 | dfs(0) 23 | 24 | 25 | for n in [1, 2, 3, 4]: 26 | main(n) 27 | -------------------------------------------------------------------------------- /recursion/leetcode/combinations/combination-sum-II-40-(distinct-combinations).py: -------------------------------------------------------------------------------- 1 | def main(nums, total): 2 | n = len(nums) 3 | nums = sorted(nums) 4 | 5 | def dfs(start, sum): 6 | if sum == total: 7 | return [[]] 8 | if sum > total: 9 | return [] 10 | res = [] 11 | for i in range(start, n): 12 | if i != start and nums[i] == nums[i - 1]: 13 | continue 14 | for path in dfs(i + 1, sum + nums[i]): 15 | res.append([nums[i]] + path) 16 | return res 17 | 18 | return dfs(0, 0) 19 | 20 | 21 | for nums, total in [ 22 | ([10, 1, 2, 7, 6, 1, 5], 8), 23 | ([2, 5, 2, 1, 2], 5) 24 | ]: 25 | print(main(nums, total)) 26 | -------------------------------------------------------------------------------- /recursion/leetcode/combinations/combination-sum-III-216-(combinations-size-k).py: -------------------------------------------------------------------------------- 1 | # T=kⁿcₖ,S=kⁿcₖ 2 | def main(k, n): 3 | total = n 4 | nums = [i for i in range(1, 10)] 5 | 6 | def dfs(start, sum, size): 7 | if sum == total and size == k: 8 | return [[]] 9 | if sum > total: 10 | return [] 11 | res = [] 12 | for i in range(start, 9): 13 | for path in dfs(i + 1, sum + nums[i], size + 1): 14 | res.append([nums[i]] + path) 15 | return res 16 | 17 | return dfs(0, 0, 0) 18 | 19 | 20 | for k, n in [ 21 | (3, 7), (3, 9), (4, 1), (3, 2), (9, 45) 22 | ]: 23 | print(main(k, n)) 24 | -------------------------------------------------------------------------------- /recursion/leetcode/combinations/combination-sum-IV-377-(count-permutations-infinite-supply).py: -------------------------------------------------------------------------------- 1 | # T=ns,S=s 2 | def main(nums, total): 3 | cache = {total: 1} 4 | 5 | def dfs(sum): 6 | if sum > total: 7 | return 0 8 | if sum in cache: 9 | return cache[sum] 10 | cache[sum] = 0 11 | for val in nums: 12 | cache[sum] += dfs(sum + val) 13 | return cache[sum] 14 | 15 | return dfs(0) 16 | 17 | 18 | for nums, total in [ 19 | ([1, 2, 3], 4), 20 | ([9], 3) 21 | ]: 22 | print(main(nums, total)) 23 | -------------------------------------------------------------------------------- /recursion/leetcode/decode-ways/get-decodings.py: -------------------------------------------------------------------------------- 1 | def construct(): 2 | decoder = {} 3 | char = 'a' 4 | for i in range(1, 27): 5 | decoder[str(i)] = char 6 | char = chr(ord(char) + 1) 7 | return decoder 8 | 9 | 10 | # T=n,S=n 11 | def main(s): 12 | cache = {'': ['']} 13 | decoder = construct() 14 | 15 | def dfs(s): 16 | if s in cache: 17 | return cache[s] 18 | if s[0] == '0': 19 | return [] 20 | cache[s] = [] 21 | for path in dfs(s[1:]): 22 | cache[s].append(decoder[s[:1]] + path) 23 | if len(s) >= 2 and int(s[:2]) <= 26: 24 | for path in dfs(s[2:]): 25 | cache[s].append(decoder[s[:2]] + path) 26 | return cache[s] 27 | 28 | return dfs(s) 29 | 30 | 31 | for s in [ 32 | '12', '123', '226', '0', '06', '27', '1201234' 33 | ]: 34 | print(main(s)) 35 | -------------------------------------------------------------------------------- /recursion/leetcode/dungeon-game-174.py: -------------------------------------------------------------------------------- 1 | def main(grid): 2 | m, n = len(grid), len(grid[0]) 3 | cache = {} 4 | 5 | def dfs(ri, ci): 6 | if ri == m - 1 and ci == n - 1: 7 | return 1 + (-grid[ri][ci]) if grid[ri][ci] <= 0 else 1 8 | if ri == m or ci == n: 9 | return float('inf') 10 | key = ri, ci 11 | if key in cache: 12 | return cache[key] 13 | cache[key] = max(min(dfs(ri + 1, ci), dfs(ri, ci + 1)) - grid[ri][ci], 1) 14 | return cache[key] 15 | 16 | return dfs(0, 0) 17 | 18 | 19 | for grid in [ 20 | [[-2, -3, 3], [-5, -10, 1], [10, 30, -5]], 21 | [[0]] 22 | ]: 23 | print(main(grid)) 24 | -------------------------------------------------------------------------------- /recursion/leetcode/iterator/examples/linked-list-iterator.py: -------------------------------------------------------------------------------- 1 | from util import build 2 | 3 | 4 | class LinkedListIterator: 5 | 6 | def __init__(self, head): 7 | self.curr = head 8 | 9 | def next(self): 10 | res = self.curr.val 11 | self.curr = self.curr.next 12 | return res 13 | 14 | def hasNext(self): 15 | return self.curr is not None 16 | 17 | 18 | lli = LinkedListIterator(build([4, 5, 7, 8, 9])) 19 | while lli.hasNext(): 20 | print(lli.next()) 21 | -------------------------------------------------------------------------------- /recursion/leetcode/iterator/examples/list-iterator.py: -------------------------------------------------------------------------------- 1 | class Iterator: 2 | def __init__(self, nums): 3 | self.pos = 0 4 | self.nums = nums 5 | 6 | def next(self): 7 | res = self.nums[self.pos] 8 | self.pos += 1 9 | return res 10 | 11 | def hasNext(self): 12 | return self.pos < len(self.nums) 13 | 14 | 15 | iterator = Iterator([4, 5, 8, 9, 12]) 16 | while iterator.hasNext(): 17 | print(iterator.next()) 18 | -------------------------------------------------------------------------------- /recursion/leetcode/iterator/examples/range-iterator.py: -------------------------------------------------------------------------------- 1 | class RangeIterator: 2 | 3 | def __init__(self, min, max): 4 | self.curr = min 5 | self.max = max 6 | 7 | def next(self): 8 | res = self.curr 9 | self.curr += 1 10 | return res 11 | 12 | def hasNext(self): 13 | return self.curr < self.max 14 | 15 | 16 | rangeIter = RangeIterator(12, 18) 17 | while rangeIter.hasNext(): 18 | print(rangeIter.next()) 19 | -------------------------------------------------------------------------------- /recursion/leetcode/iterator/examples/squares-iterator.py: -------------------------------------------------------------------------------- 1 | class SquaresIterator: 2 | 3 | def __init__(self): 4 | self.curr = 0 5 | 6 | def next(self): 7 | res = self.curr ** 2 8 | self.curr += 1 9 | return res 10 | 11 | 12 | squaresIter = SquaresIterator() 13 | for i in range(6): 14 | print(squaresIter.next()) 15 | -------------------------------------------------------------------------------- /recursion/leetcode/iterator/examples/util.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, val): 3 | self.val = val 4 | self.next = None 5 | 6 | def __str__(self): 7 | temp = self 8 | nums = [] 9 | while temp: 10 | nums.append(temp.val) 11 | temp = temp.next 12 | return str(nums) 13 | 14 | 15 | def add(head, val): 16 | if not head: 17 | return ListNode(val) 18 | temp = head 19 | while temp.next: 20 | temp = temp.next 21 | temp.next = ListNode(val) 22 | return head 23 | 24 | 25 | def build(nums): 26 | head = None 27 | for val in nums: 28 | head = add(head, val) 29 | return head if head else [] -------------------------------------------------------------------------------- /recursion/leetcode/iterator/explore.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | nums = deque([1, 2, 3]) 4 | print(nums) 5 | nums.extend([8, 7, 6]) 6 | print(nums) 7 | 8 | nums.extendleft([10, 54, 34]) 9 | print(nums) 10 | -------------------------------------------------------------------------------- /recursion/leetcode/jump-game/jump-game-III-1306-(can-reach-end-with-forward-and-backward-jumps).py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(nums, start): 3 | n = len(nums) 4 | vis = set() 5 | 6 | def dfs(i): 7 | if i < 0 or i > n - 1 or i in vis: 8 | return False 9 | if not nums[i]: 10 | return True 11 | vis.add(i) 12 | return dfs(i + nums[i]) or dfs(i - nums[i]) 13 | 14 | return dfs(start) 15 | 16 | 17 | for nums, start in [ 18 | ([4, 2, 3, 0, 3, 1, 2], 5), 19 | ([4, 2, 3, 0, 3, 1, 2], 0), 20 | ([3, 0, 2, 1, 2], 2) 21 | ]: 22 | print(main(nums, start)) 23 | -------------------------------------------------------------------------------- /recursion/leetcode/lexicographical-numbers-386.py: -------------------------------------------------------------------------------- 1 | # T=nlogn,S=1 2 | def x(n): 3 | return list(map(int, sorted(str(val) for val in range(1, n + 1)))) 4 | 5 | 6 | # T=n,S=1 7 | def y(n): 8 | res = [] 9 | 10 | def dfs(num): 11 | if num > n: 12 | return 13 | res.append(num) 14 | for digit in range(10): 15 | dfs(10 * num + digit) 16 | 17 | for num in range(1, 10): 18 | dfs(num) 19 | return res 20 | 21 | 22 | for n in [ 23 | 2, 13, 24 24 | ]: 25 | print(x(n)) 26 | print(y(n)) 27 | -------------------------------------------------------------------------------- /recursion/leetcode/n-queens/n-queens-52.py: -------------------------------------------------------------------------------- 1 | # T=n!,S=n² 2 | def main(n): 3 | cols = set() 4 | diags = set() 5 | antidiags = set() 6 | 7 | def choose(ri, ci): 8 | cols.add(ci) 9 | diags.add(ri - ci) 10 | antidiags.add(ri + ci) 11 | 12 | def discard(ri, ci): 13 | cols.remove(ci) 14 | diags.remove(ri - ci) 15 | antidiags.remove(ri + ci) 16 | 17 | def dfs(ri): 18 | if ri == n: 19 | return 1 20 | res = 0 21 | for ci in range(n): 22 | if ci in cols or ri - ci in diags or ri + ci in antidiags: 23 | continue 24 | choose(ri, ci) 25 | res += dfs(ri + 1) 26 | discard(ri, ci) 27 | return res 28 | 29 | return dfs(0) 30 | 31 | 32 | for n in [1, 2, 3, 4, 5]: 33 | print(main(n)) 34 | -------------------------------------------------------------------------------- /recursion/leetcode/palindrome-partitioning/count.py: -------------------------------------------------------------------------------- 1 | def isPalindrome(s): 2 | return s == s[::-1] 3 | 4 | 5 | def main(s): 6 | def dfs(s): 7 | if not s: 8 | return 1 9 | res = 0 10 | for i in range(len(s)): 11 | prefix, suffix = s[:i + 1], s[i + 1:] 12 | if isPalindrome(prefix): 13 | res += dfs(suffix) 14 | return res 15 | 16 | return dfs(s) 17 | 18 | 19 | for s in [ 20 | 'abaaba', 21 | 'nitin', 22 | 'geeks', 23 | 'aab', 24 | 'aaa', 25 | 'a' 26 | ]: 27 | print(main(s)) 28 | -------------------------------------------------------------------------------- /recursion/leetcode/palindrome-partitioning/find.py: -------------------------------------------------------------------------------- 1 | def isPalindrome(s): 2 | return s == s[::-1] 3 | 4 | 5 | def main(s): 6 | def dfs(s): 7 | if not s: 8 | return True 9 | for i in range(len(s)): 10 | prefix, suffix = s[:i + 1], s[i + 1:] 11 | if isPalindrome(prefix) and dfs(suffix): 12 | return True 13 | return False 14 | 15 | return dfs(s) 16 | 17 | 18 | for s in [ 19 | 'nitin', 20 | 'geeks', 21 | 'aab', 22 | 'a' 23 | ]: 24 | print(main(s)) 25 | -------------------------------------------------------------------------------- /recursion/leetcode/parentheses/generate-parentheses-22.py: -------------------------------------------------------------------------------- 1 | def main(n): 2 | res = [] 3 | 4 | def dfs(open, close, path): 5 | if open == close == n: 6 | res.append(path) 7 | return 8 | if open < n: 9 | dfs(open + 1, close, path + '(') 10 | if close < open: 11 | dfs(open, close + 1, path + ')') 12 | 13 | dfs(0, 0, '') 14 | return res 15 | 16 | 17 | for n in [1, 2, 3]: 18 | print(main(n)) 19 | -------------------------------------------------------------------------------- /recursion/leetcode/parentheses/minimum-remove-to-make-valid-parentheses-1249.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(s): 3 | n = len(s) 4 | stack = [] 5 | indicesToRemove = set() 6 | for i in range(n): 7 | if s[i] != ')': 8 | stack.append(i) 9 | else: 10 | while stack and s[stack[-1]] != '(': 11 | stack.pop() 12 | if not stack: 13 | indicesToRemove.add(i) 14 | else: 15 | stack.pop() 16 | indicesToRemove.update({i for i in stack if s[i] == '('}) 17 | return ''.join(s[i] for i in range(n) if i not in indicesToRemove) 18 | 19 | 20 | for s in [ 21 | 'lee(t(c)o)de)', 22 | 'a)b(c)d', 23 | '))((', 24 | '(a(b(c)d)' 25 | ]: 26 | print(main(s)) 27 | -------------------------------------------------------------------------------- /recursion/leetcode/perfect-squares-279-(minimum-elements-infinite-supply).py: -------------------------------------------------------------------------------- 1 | from math import sqrt 2 | 3 | 4 | # T=n√n,S=n 5 | def main(n): 6 | nums = [i ** 2 for i in range(1, int(sqrt(n)) + 1)] 7 | total = n 8 | size = len(nums) 9 | cache = [[float('inf')] * (total + 1) for _ in range(size)] 10 | for j in range(total + 1): 11 | quot, rem = divmod(j, nums[0]) 12 | cache[0][j] = quot if not rem else float('inf') 13 | for i in range(size): 14 | cache[i][0] = 0 15 | for i in range(1, size): 16 | for j in range(1, total + 1): 17 | cache[i][j] = min(cache[i - 1][j], 1 + (cache[i][j - nums[i]] if j >= nums[i] else float('inf'))) 18 | return cache[-1][-1] 19 | 20 | 21 | for n in [ 22 | 12, 13, 40, 143, 6554, 7168 23 | ]: 24 | print(main(n)) 25 | -------------------------------------------------------------------------------- /recursion/leetcode/permutations/palindrome-permutation-266.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=1 5 | def x(s): 6 | oddFrequencyCount = 0 7 | for count in Counter(s).values(): 8 | oddFrequencyCount += count % 2 9 | if oddFrequencyCount > 1: 10 | return False 11 | return True 12 | 13 | 14 | # T=n,S=1 15 | def y(s): 16 | return sum(count % 2 for count in Counter(s).values() if count % 2) < 2 17 | 18 | 19 | # T=n,S=1 20 | def z(s): 21 | chars = set() 22 | for char in s: 23 | if char in chars: 24 | chars.remove(char) 25 | else: 26 | chars.add(char) 27 | return len(chars) in [0, 1] 28 | 29 | 30 | for s in [ 31 | 'a', 32 | 'aaa', 33 | 'code', 34 | 'aab', 35 | 'civil', 36 | 'carerac', 37 | 'aabbccc' 38 | ]: 39 | print(x(s), end=' ') 40 | print(y(s), end=' ') 41 | print(z(s)) 42 | -------------------------------------------------------------------------------- /recursion/leetcode/prefix-and-suffix.py: -------------------------------------------------------------------------------- 1 | def prefixSuffix1(s): 2 | for i in range(len(s)): 3 | prefix, suffix = s[:i], s[i:] 4 | print(prefix, suffix) 5 | print() 6 | 7 | 8 | def prefixSuffix2(s): 9 | for i in range(len(s)): 10 | prefix, suffix = s[:i + 1], s[i + 1:] 11 | print(prefix, suffix) 12 | print() 13 | 14 | 15 | for s in [ 16 | 'abcd', 17 | 'ni', 18 | 'aa', 19 | 'n', 20 | ]: 21 | prefixSuffix1(s) 22 | prefixSuffix2(s) 23 | 24 | s = 'abcd' 25 | for i in range(len(s) + 1): 26 | print(s[:i], s[i:]) 27 | -------------------------------------------------------------------------------- /recursion/leetcode/remove-one-character.py: -------------------------------------------------------------------------------- 1 | s = 'abcd' 2 | n = len(s) 3 | for i in range(n): 4 | print(s[:i] + s[i + 1:], end='|') 5 | -------------------------------------------------------------------------------- /recursion/leetcode/subsets/maximum-product.py: -------------------------------------------------------------------------------- 1 | def main(nums): 2 | n = len(nums) 3 | 4 | def dfs(i, sum1, sum2): 5 | if i == n: 6 | return sum1 * sum2 7 | return max(dfs(i + 1, sum1 + nums[i], sum2), dfs(i + 1, sum1, sum2 + nums[i])) 8 | 9 | return dfs(0, 0, 0) 10 | 11 | 12 | for nums in [ 13 | [1, 2, 3, 4, 5] 14 | ]: 15 | print(main(nums)) 16 | -------------------------------------------------------------------------------- /recursion/leetcode/subsets/minimum-subset-sum-difference.py: -------------------------------------------------------------------------------- 1 | # T=2ⁿ,S=n 2 | def x(nums): 3 | n = len(nums) 4 | 5 | def dfs(i, sum1, sum2): 6 | if i == n: 7 | return abs(sum1 - sum2) 8 | return min(dfs(i + 1, sum1 + nums[i], sum2), dfs(i + 1, sum1, sum2 + nums[i])) 9 | 10 | return dfs(0, 0, 0) 11 | 12 | 13 | # T=ns,S=ns 14 | def y(nums): 15 | n = len(nums) 16 | cache = {} 17 | 18 | def dfs(i, sum1, sum2): 19 | if i == n: 20 | return abs(sum1 - sum2) 21 | key = i, sum1, sum2 22 | if key in cache: 23 | return cache[key] 24 | cache[key] = min(dfs(i + 1, sum1 + nums[i], sum2), dfs(i + 1, sum1, sum2 + nums[i])) 25 | return cache[key] 26 | 27 | return dfs(0, 0, 0) 28 | 29 | 30 | for nums in [ 31 | [1, 2, 3, 9], 32 | [1, 2, 7, 1, 5], 33 | [1, 3, 100, 4] 34 | ]: 35 | print(x(nums), end=' ') 36 | print(y(nums)) 37 | -------------------------------------------------------------------------------- /recursion/leetcode/subsets/partition-into-k-subsets/count-combinations-n-elements-k-non-empty-subsets.py: -------------------------------------------------------------------------------- 1 | # T=nk,S=nk 2 | def main(n, k): 3 | cache = {} 4 | 5 | def dfs(n, k): 6 | if not n or not k or k > n: 7 | return 0 8 | if n == k or k == 1: 9 | return 1 10 | key = n, k 11 | if key in cache: 12 | return cache[key] 13 | cache[key] = dfs(n - 1, k - 1) + k * dfs(n - 1, k) 14 | return cache[key] 15 | 16 | return dfs(n, k) 17 | 18 | 19 | for n, k in [ 20 | (4, 3), (3, 2), (3, 1) 21 | ]: 22 | print(main(n, k)) 23 | -------------------------------------------------------------------------------- /recursion/leetcode/subsets/partition-into-k-subsets/partition-to-k-equal-sum-subsets-698.py: -------------------------------------------------------------------------------- 1 | # S=n 2 | def main(nums, k): 3 | if sum(nums) % k != 0: 4 | return False 5 | n, total, vis = len(nums), sum(nums) // k, set() 6 | 7 | def dfs(start, sum, k): 8 | if k == 1: 9 | return True 10 | if sum == total: 11 | return dfs(0, 0, k - 1) 12 | if sum > total: 13 | return False 14 | for i in range(start, n): 15 | if i in vis: 16 | continue 17 | vis.add(i) 18 | if dfs(i + 1, sum + nums[i], k): 19 | return True 20 | vis.remove(i) 21 | return False 22 | 23 | return dfs(0, 0, k) 24 | 25 | 26 | for nums, k in [ 27 | ([1, 1, 1, 1, 2, 2, 2, 2], 2), 28 | ([4, 3, 2, 3, 5, 2, 1], 4), 29 | ([1, 2, 3, 4], 3), 30 | ]: 31 | print(main(nums, k)) 32 | -------------------------------------------------------------------------------- /recursion/leetcode/subsets/target-sum-494.py: -------------------------------------------------------------------------------- 1 | # T=ns,S=ns 2 | def main(nums, total): 3 | n = len(nums) 4 | cache = {} 5 | 6 | def dfs(i, sum): 7 | if i == n: 8 | return int(sum == total) 9 | key = i, sum 10 | if key in cache: 11 | return cache[key] 12 | cache[key] = dfs(i + 1, sum + nums[i]) + dfs(i + 1, sum - nums[i]) 13 | return cache[key] 14 | 15 | return dfs(0, 0) 16 | 17 | 18 | for nums, total in [ 19 | ([1, 1, 1, 1, 1], 3), 20 | ([1, 0], 1), 21 | ([1], 1) 22 | ]: 23 | print(main(nums, total)) 24 | -------------------------------------------------------------------------------- /recursion/leetcode/subsets/tug-of-war.py: -------------------------------------------------------------------------------- 1 | def main(nums): 2 | n = len(nums) 3 | res = [float('inf'), None, None] 4 | path1 = [] 5 | path2 = [] 6 | 7 | def dfs(i, sum1, sum2): 8 | nonlocal res 9 | if i == n: 10 | diff = abs(sum1 - sum2) 11 | if diff < res[0]: 12 | res[0] = diff 13 | res[1] = path1[:] 14 | res[2] = path2[:] 15 | return 16 | if len(path1) < (n + 1) // 2: 17 | path1.append(nums[i]) 18 | dfs(i + 1, sum1 + nums[i], sum2) 19 | path1.pop() 20 | if len(path2) < (n + 1) // 2: 21 | path2.append(nums[i]) 22 | dfs(i + 1, sum1, sum2 + nums[i]) 23 | path2.pop() 24 | 25 | dfs(0, 0, 0) 26 | return res 27 | 28 | 29 | for nums in [ 30 | [1, 2, 3, 4, 5, 6], 31 | [1, 2, 3, 4, 5], 32 | ]: 33 | print(main(nums)) 34 | -------------------------------------------------------------------------------- /recursion/leetcode/unique-paths/unique-paths-62.py: -------------------------------------------------------------------------------- 1 | def x(m, n): 2 | cache = {(m - 1, n - 1): 1} 3 | 4 | def dfs(ri, ci): 5 | if ri == m or ci == n: 6 | return 0 7 | key = ri, ci 8 | if key in cache: 9 | return cache[key] 10 | cache[key] = 0 11 | for i, j in (1, 0), (0, 1): 12 | cache[key] += dfs(ri + i, ci + j) 13 | return cache[key] 14 | 15 | return dfs(0, 0) 16 | 17 | 18 | def y(m, n): 19 | cache = [[1 for _ in range(n)] for _ in range(m)] 20 | for i in range(1, m): 21 | for j in range(1, n): 22 | cache[i][j] = cache[i - 1][j] + cache[i][j - 1] 23 | return cache[-1][-1] 24 | 25 | 26 | for m, n in [ 27 | (3, 7), 28 | (3, 2), 29 | (7, 3), 30 | (3, 3) 31 | ]: 32 | print(x(m, n), end=' ') 33 | print(y(m, n)) 34 | -------------------------------------------------------------------------------- /recursion/leetcode/word-pattern/word-pattern-290.py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(pattern, s): 3 | words = s.split() 4 | if len(pattern) != len(words): 5 | return False 6 | n = len(pattern) 7 | letterToWord = {} 8 | wordToLetter = {} 9 | for i in range(n): 10 | letter = pattern[i] 11 | word = words[i] 12 | if letter in letterToWord and letterToWord[letter] != word: 13 | return False 14 | letterToWord[letter] = word 15 | for i in range(n): 16 | letter = pattern[i] 17 | word = words[i] 18 | if word in wordToLetter and wordToLetter[word] != letter: 19 | return False 20 | wordToLetter[word] = letter 21 | return True 22 | 23 | 24 | for pattern, s in [ 25 | ('abba', 'dog cat cat dog'), 26 | ('abba', 'dog cat cat fish'), 27 | ('aaaa', 'dog cat cat dog'), 28 | ('abba', 'dog dog dog dog') 29 | ]: 30 | print(main(pattern, s)) 31 | -------------------------------------------------------------------------------- /recursion/non-adjacent-subsets/count.py: -------------------------------------------------------------------------------- 1 | def x(nums): 2 | n = len(nums) 3 | 4 | def dfs(i): 5 | if i >= n: 6 | return 1 7 | return dfs(i + 2) + dfs(i + 1) 8 | 9 | return dfs(0) 10 | 11 | 12 | def y(nums): 13 | n = len(nums) 14 | 15 | def dfs(start): 16 | count = 1 17 | for i in range(start, n): 18 | count += dfs(i + 2) 19 | return count 20 | 21 | return dfs(0) 22 | 23 | 24 | for nums in [ 25 | [1, 2, 3] 26 | ]: 27 | print(x(nums)) 28 | print(y(nums)) 29 | -------------------------------------------------------------------------------- /recursion/non-adjacent-subsets/get.py: -------------------------------------------------------------------------------- 1 | def x(nums): 2 | n = len(nums) 3 | 4 | def dfs(i, path): 5 | if i >= n: 6 | return [path] 7 | return dfs(i + 2, path + [nums[i]]) + dfs(i + 1, path) 8 | 9 | return dfs(0, []) 10 | 11 | 12 | def y(nums): 13 | n = len(nums) 14 | res = [] 15 | 16 | def dfs(start, path): 17 | res.append(path) 18 | for i in range(start, n): 19 | dfs(i + 2, path + [nums[i]]) 20 | 21 | dfs(0, []) 22 | return res 23 | 24 | 25 | for nums in [ 26 | [1, 2, 3] 27 | ]: 28 | print(x(nums)) 29 | print(y(nums)) 30 | -------------------------------------------------------------------------------- /recursion/subset-sum/knapsack/count.py: -------------------------------------------------------------------------------- 1 | def x(nums, total): 2 | n = len(nums) 3 | 4 | def dfs(i, sum): 5 | if sum > total: 6 | return 0 7 | if sum == total or i == n: 8 | return 1 9 | inc = dfs(i + 1, sum + nums[i]) 10 | exc = dfs(i + 1, sum) 11 | return inc + exc 12 | 13 | return dfs(0, 0) 14 | 15 | 16 | for nums, total in [ 17 | ([1, 2, 3], 5), 18 | ([2, 5, 1, 3, 4], 7), 19 | ([1, 2, 3, 8, 7, 4], 10), 20 | ([1, 2, 3], 6), 21 | ([7, 3, 5], 8) 22 | ]: 23 | print(x(nums, total)) 24 | -------------------------------------------------------------------------------- /recursion/subsets/leetcode/subsets-78.py: -------------------------------------------------------------------------------- 1 | # T=n2ⁿ,S=n2ⁿ⁻¹ 2 | def main(nums): 3 | n = len(nums) 4 | res = [] 5 | path = [] 6 | 7 | def dfs(start): 8 | res.append(path[:]) 9 | for i in range(start, n): 10 | path.append(nums[i]) 11 | dfs(i + 1) 12 | path.pop() 13 | 14 | dfs(0) 15 | return res 16 | 17 | 18 | for nums in [ 19 | [], 20 | [1], 21 | [1, 2, 3] 22 | ]: 23 | print(main(nums)) 24 | -------------------------------------------------------------------------------- /recursion/subsets/subsets-iterative.py: -------------------------------------------------------------------------------- 1 | def x(nums): 2 | n = len(nums) 3 | res = [] 4 | for i in range(2 ** n): 5 | val = [] 6 | for j in range(n): 7 | if i & (1 << j): 8 | val.append(nums[j]) 9 | res.append(val) 10 | return res 11 | 12 | 13 | def y(nums): 14 | n = len(nums) 15 | res = [] 16 | for i in range(2 ** n): 17 | num = i 18 | val = [] 19 | for j in range(n): 20 | rem = num % 2 21 | num //= 2 22 | if rem: 23 | val.append(nums[j]) 24 | res.append(val) 25 | return res 26 | 27 | 28 | for nums in [ 29 | [1, 2, 3] 30 | ]: 31 | print(str(nums) + '->' + str(x(nums))) 32 | print(str(nums) + '->' + str(y(nums))) 33 | -------------------------------------------------------------------------------- /sliding-window/count/count-substrings-with-at-most-k-distinct-characters.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=1 5 | def main(s, k): 6 | freq = Counter() 7 | res = left = 0 8 | for right in range(len(s)): 9 | freq[s[right]] += 1 10 | while len(freq) > k: 11 | freq[s[left]] -= 1 12 | if not freq[s[left]]: 13 | del freq[s[left]] 14 | left += 1 15 | res += (right - left + 1) 16 | return res 17 | 18 | 19 | for s, k in [ 20 | ('aaaaa', 3), 21 | ('aabcbcdbca', 2), 22 | ('aabacbebebe', 3), 23 | ('ddacbbaccdedacebb', 3), 24 | ('loveleetcode', 4), 25 | ('aaabc', 2), 26 | ('eceba', 2), 27 | ('aa', 1), 28 | ]: 29 | print(main(s, k)) 30 | -------------------------------------------------------------------------------- /sliding-window/count/subarrays-with-k-different-integers-992-(count).py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=1 5 | def main(nums, k): 6 | def atMost(k): 7 | freq = Counter() 8 | res = left = 0 9 | for right in range(len(nums)): 10 | freq[nums[right]] += 1 11 | while len(freq) > k: 12 | freq[nums[left]] -= 1 13 | if not freq[nums[left]]: 14 | del freq[nums[left]] 15 | left += 1 16 | res += (right - left + 1) 17 | return res 18 | 19 | return atMost(k) - atMost(k - 1) 20 | 21 | 22 | for nums, k in [ 23 | ([1, 2, 1, 2, 3], 2), 24 | ([1, 2, 1, 3, 4], 3) 25 | ]: 26 | print(main(nums, k)) 27 | -------------------------------------------------------------------------------- /sliding-window/longest-substring/longest-substring-with-at-most-k-distinct-characters-340.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=1 5 | def main(s, k): 6 | freq = Counter() 7 | res = left = 0 8 | for right in range(len(s)): 9 | freq[s[right]] += 1 10 | while len(freq) > k: 11 | freq[s[left]] -= 1 12 | if not freq[s[left]]: 13 | del freq[s[left]] 14 | left += 1 15 | res = max(res, right - left + 1) 16 | return res 17 | 18 | 19 | for s, k in [ 20 | ('aaaaa', 3), 21 | ('aabacbebebe', 3), 22 | ('ddacbbaccdedacebb', 3), 23 | ('loveleetcode', 4), 24 | ('aaabc', 2), 25 | ('eceba', 2), 26 | ('aa', 1), 27 | ]: 28 | print(main(s, k)) 29 | -------------------------------------------------------------------------------- /sliding-window/longest-substring/longest-substring-with-at-most-two-distinct-characters-159.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=1 5 | def main(s): 6 | freq = Counter() 7 | res = left = 0 8 | for right in range(len(s)): 9 | freq[s[right]] += 1 10 | while len(freq) > 2: 11 | char = s[left] 12 | freq[char] -= 1 13 | if not freq[char]: 14 | del freq[char] 15 | left += 1 16 | res = max(res, right - left + 1) 17 | return res 18 | 19 | 20 | for s in [ 21 | 'eceba', 22 | 'ccaabbb' 23 | ]: 24 | print(main(s)) 25 | -------------------------------------------------------------------------------- /sliding-window/longest-substring/longest-substring-with-k-distinct-characters.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=1 5 | def main(s, k): 6 | freq = Counter() 7 | res = left = 0 8 | for right in range(len(s)): 9 | freq[s[right]] += 1 10 | while len(freq) > k: 11 | char = s[left] 12 | freq[char] -= 1 13 | if not freq[char]: 14 | del freq[char] 15 | left += 1 16 | if len(freq) == k: 17 | res = max(res, right - left + 1) 18 | return res 19 | 20 | 21 | for s, k in [ 22 | ('aaaaa', 3), 23 | ('aabacbebebe', 3), 24 | ('aabacbebebe', 3), 25 | ('ddacbbaccdedacebb', 3), 26 | ('loveleetcode', 4), 27 | ('aaabc', 2), 28 | ('eceba', 2), 29 | ('aa', 1), 30 | ]: 31 | print(main(s, k)) 32 | -------------------------------------------------------------------------------- /sliding-window/max-consecutive-ones/max-consecutive-ones-485-(length-of-the-longest-subarray-with-all-1s).py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(nums): 3 | res = count = 0 4 | for val in nums: 5 | if val: 6 | count += 1 7 | else: 8 | res = max(res, count) 9 | count = 0 10 | return max(res, count) 11 | 12 | 13 | for nums in [ 14 | [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1], 15 | [1, 1, 0, 1, 1, 1], 16 | [1, 0, 1, 1, 0, 1], 17 | [0, 0, 0], 18 | [0], 19 | [1], 20 | ]: 21 | print(main(nums), end=' ') 22 | -------------------------------------------------------------------------------- /sliding-window/max-consecutive-ones/max-consecutive-ones-II-487-(length-of-the-longest-subarray-with-all-1s-having-at-most-one-0).py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(nums): 3 | res = left = zeros = 0 4 | for right in range(len(nums)): 5 | zeros += (nums[right] == 0) 6 | while zeros > 1: 7 | zeros -= (nums[left] == 0) 8 | left += 1 9 | res = max(res, right - left + 1) 10 | return res 11 | 12 | 13 | for nums in [ 14 | [1], 15 | [0], 16 | [1, 0, 1, 1, 0], 17 | [1, 0, 1, 1, 0, 1] 18 | ]: 19 | print(main(nums)) 20 | -------------------------------------------------------------------------------- /sliding-window/max-consecutive-ones/max-consecutive-ones-III-1004-(length-of-the-longest-subarray-with-all-1s-having-at-most-k-0s).py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(nums, k): 3 | res = left = zeros = 0 4 | for right in range(len(nums)): 5 | zeros += (nums[right] == 0) 6 | while zeros > k: 7 | zeros -= (nums[left] == 0) 8 | left += 1 9 | res = max(res, right - left + 1) 10 | return res 11 | 12 | 13 | for nums, k in [ 14 | ([1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0], 2), 15 | ([0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1], 3), 16 | ([1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1], 2) 17 | ]: 18 | print(main(nums, k)) 19 | -------------------------------------------------------------------------------- /sliding-window/minimize/degree-of-an-array-697.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=n 5 | def main(nums): 6 | res = n = len(nums) 7 | degree = max(Counter(nums).values()) 8 | freq = Counter() 9 | left = 0 10 | for right in range(n): 11 | freq[nums[right]] += 1 12 | while freq[nums[right]] == degree: 13 | res = min(res, right - left + 1) 14 | freq[nums[left]] -= 1 15 | left += 1 16 | return res 17 | 18 | 19 | for nums in [ 20 | [1, 2, 2, 3, 1], 21 | [1, 2, 2, 3, 1, 4, 2], 22 | [1, 3, 2, 4, 2, 3, 4, 2, 5, 6, 5, 5, 7] 23 | ]: 24 | print(main(nums)) 25 | -------------------------------------------------------------------------------- /sliding-window/minimize/smallest-substring-of-a-string-containing-all-unique-characters-of-itself.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | 4 | # T=n,S=n 5 | def main(s): 6 | res = n = len(s) 7 | unique = len(set(s)) 8 | freq = Counter() 9 | left = 0 10 | for right in range(n): 11 | freq[s[right]] += 1 12 | while len(freq) == unique: 13 | res = min(res, right - left + 1) 14 | freq[s[left]] -= 1 15 | if not freq[s[left]]: 16 | del freq[s[left]] 17 | left += 1 18 | return res 19 | 20 | 21 | for s in [ 22 | 'bbacacdcbbcaadcdca', 23 | 'aabcbcdbca', 24 | 'aaab' 25 | ]: 26 | print(main(s)) 27 | -------------------------------------------------------------------------------- /sorting/leetcode/minimum-absolute-difference-1200.py: -------------------------------------------------------------------------------- 1 | # T=nlogn 2 | def x(nums): 3 | nums.sort() 4 | n = len(nums) 5 | least = float('inf') 6 | res = [] 7 | for i in range(n - 1): 8 | least = min(least, nums[i + 1] - nums[i]) 9 | for i in range(n - 1): 10 | if nums[i + 1] - nums[i] == least: 11 | res.append([nums[i], nums[i + 1]]) 12 | return res 13 | 14 | 15 | # T=nlogn 16 | def y(nums): 17 | nums.sort() 18 | n = len(nums) 19 | least = min(nums[i + 1] - nums[i] for i in range(n - 1)) 20 | return [[nums[i], nums[i + 1]] for i in range(n - 1) if nums[i + 1] - nums[i] == least] 21 | 22 | 23 | for nums in [ 24 | [4, 2, 1, 3], 25 | [1, 3, 6, 10, 15], 26 | [3, 8, -10, 23, 19, -4, -14, 27] 27 | ]: 28 | print(x(nums)) 29 | print(y(nums)) 30 | -------------------------------------------------------------------------------- /sorting/leetcode/reorder-data-in-log-files-937.py: -------------------------------------------------------------------------------- 1 | # T=mnlogn,S=mn 2 | def main(logs): 3 | def sort(log): 4 | if log[-1].isnumeric(): 5 | return 1, 6 | identifier, content = log.split(' ', maxsplit=1) 7 | return 0, content, identifier 8 | 9 | return sorted(logs, key=sort) 10 | 11 | 12 | for logs in [ 13 | ['dig1 8 1 5 1', 'let1 art can', 'dig2 3 6', 'let2 own kit dig', 'let3 art zero'], 14 | ['a1 9 2 3 1', 'g1 act car', 'zo4 4 7', 'ab1 off key dog', 'a8 act zoo'], 15 | ['a1 9 2 3 1', 'g1 act car', 'zo4 4 7', 'ab1 off key dog', 'a8 act zoo', 'a2 act car'], 16 | ['27 85717 7', '2 y xyr fc', '52 314 99', 'd 046099 0', 'm azv x f', '7e apw c y', '8 hyyq z p', '6 3272401', 17 | 'c otdk cl', '8 ksif m u'] 18 | ]: 19 | print(main(logs)) 20 | -------------------------------------------------------------------------------- /sorting/selection-sort.py: -------------------------------------------------------------------------------- 1 | # T=n²,S=1 2 | def main(nums): 3 | n = len(nums) 4 | for i in range(n - 1): 5 | minIndex = i 6 | for j in range(i + 1, n): 7 | if nums[j] < nums[minIndex]: 8 | minIndex = j 9 | nums[i], nums[minIndex] = nums[minIndex], nums[i] 10 | return nums 11 | 12 | 13 | for nums in [ 14 | [1, 2, 3, 4, 5], 15 | [64, 25, 12, 22, 11], 16 | [5, 4, 3, 2, 1] 17 | ]: 18 | print(main(nums)) 19 | -------------------------------------------------------------------------------- /stacks/concept/monotonic/decreasing-stack.py: -------------------------------------------------------------------------------- 1 | inputs = [ 2 | [7, 6, 6, 5, 5, 4, 3, 2, 1], 3 | [7, 6, 6, 5, 5, 4, 3, 8, 2, 1], 4 | [5, 4, 3, 2, 1], 5 | [5, 4, 1, 2, 3], 6 | [4, 5, 6, 1, 2], 7 | [1, 2, 3, 4, 5], 8 | [1, 2, 3, 4, 3, 5, 1] 9 | ] 10 | 11 | 12 | # T=n,S=n 13 | def main(nums): 14 | stack = [] 15 | for val in nums: 16 | while stack and stack[-1] <= val: 17 | stack.pop() 18 | stack.append(val) 19 | return stack 20 | 21 | 22 | for nums in inputs: 23 | print(main(nums)) 24 | 25 | print() 26 | 27 | 28 | # T=n,S=n 29 | def main(nums): 30 | stack = [] 31 | for i in range(len(nums)): 32 | while stack and nums[stack[-1]] <= nums[i]: 33 | stack.pop() 34 | stack.append(i) 35 | return stack 36 | 37 | 38 | for nums in inputs: 39 | print(main(nums)) 40 | -------------------------------------------------------------------------------- /stacks/concept/monotonic/increasing-stack.py: -------------------------------------------------------------------------------- 1 | inputs = [ 2 | [7, 6, 6, 5, 5, 4, 3, 2, 1], 3 | [7, 6, 6, 5, 5, 4, 3, 8, 2, 1], 4 | [5, 4, 3, 2, 1], 5 | [5, 4, 1, 2, 3], 6 | [4, 5, 6, 1, 2], 7 | [1, 2, 3, 4, 5], 8 | [1, 2, 3, 4, 3, 5, 1] 9 | ] 10 | 11 | 12 | # T=n,S=n 13 | def main(nums): 14 | stack = [] 15 | for val in nums: 16 | while stack and stack[-1] >= val: 17 | stack.pop() 18 | stack.append(val) 19 | return stack 20 | 21 | 22 | for nums in inputs: 23 | print(main(nums)) 24 | 25 | print() 26 | 27 | 28 | # T=n,S=n 29 | def main(nums): 30 | stack = [] 31 | for i in range(len(nums)): 32 | while stack and nums[stack[-1]] >= nums[i]: 33 | stack.pop() 34 | stack.append(i) 35 | return stack 36 | 37 | 38 | for nums in inputs: 39 | print(main(nums)) 40 | -------------------------------------------------------------------------------- /stacks/concept/next-greater/next-greater-left.py: -------------------------------------------------------------------------------- 1 | inputs = [ 2 | [5, 3, 1, 2, 4], 3 | [1, 1, 2, 3, 4, 4] 4 | ] 5 | 6 | 7 | # T=n,S=n 8 | def main(nums): 9 | res = [] 10 | stack = [] 11 | for val in nums: 12 | while stack and stack[-1] <= val: 13 | stack.pop() 14 | res.append(stack[-1] if stack else -1) 15 | stack.append(val) 16 | return res 17 | 18 | 19 | for nums in inputs: 20 | print(main(nums)) 21 | 22 | print() 23 | 24 | 25 | # T=n,S=n 26 | # res[i] = distance of next greater element on left of nums[i] 27 | def main(nums): 28 | res = [] 29 | stack = [] 30 | for i in range(len(nums)): 31 | while stack and nums[stack[-1]] <= nums[i]: 32 | stack.pop() 33 | res.append(i - stack[-1] if stack else -1) 34 | stack.append(i) 35 | return res 36 | 37 | 38 | for nums in inputs: 39 | print(main(nums)) 40 | -------------------------------------------------------------------------------- /stacks/concept/next-smaller/next-smaller-left.py: -------------------------------------------------------------------------------- 1 | inputs = [ 2 | [5, 3, 1, 2, 4], 3 | [1, 1, 2, 3, 4, 4] 4 | ] 5 | 6 | 7 | # T=n,S=n 8 | def main(nums): 9 | res = [] 10 | stack = [] 11 | for val in nums: 12 | while stack and stack[-1] >= val: 13 | stack.pop() 14 | res.append(stack[-1] if stack else -1) 15 | stack.append(val) 16 | return res 17 | 18 | 19 | for nums in inputs: 20 | print(main(nums)) 21 | 22 | print() 23 | 24 | 25 | # T=n,S=n 26 | # res[i] = distance of next smaller element on left of nums[i] 27 | def main(nums): 28 | res = [] 29 | stack = [] 30 | for i in range(len(nums)): 31 | while stack and nums[stack[-1]] >= nums[i]: 32 | stack.pop() 33 | res.append(i - stack[-1] if stack else -1) 34 | stack.append(i) 35 | return res 36 | 37 | 38 | for nums in inputs: 39 | print(main(nums)) 40 | -------------------------------------------------------------------------------- /stacks/concept/next-smaller/next-smaller-right.py: -------------------------------------------------------------------------------- 1 | inputs = [ 2 | [5, 3, 1, 2, 4], 3 | [1, 1, 2, 3, 4, 4] 4 | ] 5 | 6 | 7 | # T=n,S=n 8 | def main(nums): 9 | res = [-1] * len(nums) 10 | stack = [] 11 | for i in range(len(nums)): 12 | while stack and nums[stack[-1]] > nums[i]: 13 | res[stack.pop()] = nums[i] 14 | stack.append(i) 15 | return res 16 | 17 | 18 | for nums in inputs: 19 | print(main(nums)) 20 | 21 | print() 22 | 23 | 24 | # T=n,S=n 25 | # res[i] = distance of next smaller element on right of nums[i] 26 | def main(nums): 27 | res = [-1] * len(nums) 28 | stack = [] 29 | for i in range(len(nums)): 30 | while stack and nums[stack[-1]] > nums[i]: 31 | res[stack.pop()] = i - stack[-1] 32 | stack.append(i) 33 | return res 34 | 35 | 36 | for nums in inputs: 37 | print(main(nums)) 38 | -------------------------------------------------------------------------------- /stacks/explore.py: -------------------------------------------------------------------------------- 1 | print(max([4, 5, 6], default=10)) 2 | print(max([], default=10)) 3 | 4 | s = ' 242-100 + 2 ' 5 | print(s.replace(' ', '')) 6 | 7 | nums = [1, 2, 3] 8 | 9 | print(nums.pop(), nums.pop()) 10 | 11 | print(int(-13 / 4)) 12 | 13 | 14 | def foo(num): 15 | print(type(num)) 16 | 17 | 18 | foo(int('5')) 19 | 20 | print('-11'.isdigit()) 21 | 22 | print(int('-11')) 23 | -------------------------------------------------------------------------------- /stacks/leetcode/asteroid-collision-735.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(nums): 3 | n = len(nums) 4 | i = 0 5 | stack = [] 6 | while i < n: 7 | while stack and i < n and nums[i] < 0 < stack[-1]: 8 | if stack[-1] < abs(nums[i]): 9 | stack.pop() 10 | elif stack[-1] > abs(nums[i]): 11 | i += 1 12 | else: 13 | stack.pop() 14 | i += 1 15 | if i < n: 16 | stack.append(nums[i]) 17 | i += 1 18 | return stack 19 | 20 | 21 | for nums in [ 22 | [-1, -2, 1, 3, 1, 2, -3, 3], 23 | [-2, 2, -1, -2], 24 | [-2, 1, -1, -2], 25 | [5, 10, -5], 26 | [10, 2, -5], 27 | [8, -8], 28 | ]: 29 | print(main(nums)) 30 | -------------------------------------------------------------------------------- /stacks/leetcode/increasing/remove-k-digits-402.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(num, k): 3 | if len(num) == k: 4 | return '0' 5 | stack = [] 6 | for digit in num: 7 | while k and stack and stack[-1] > digit: 8 | stack.pop() 9 | k -= 1 10 | stack.append(digit) 11 | while k: 12 | stack.pop() 13 | k -= 1 14 | return ''.join(stack).lstrip('0') or '0' 15 | 16 | 17 | for num, k in [ 18 | ('12345264', 4), 19 | ('1432219', 3), 20 | ('10200', 1), 21 | ('10', 2), 22 | ('112', 1), 23 | ('10', 1), 24 | ('100', 1) 25 | ]: 26 | print(main(num, k)) 27 | -------------------------------------------------------------------------------- /stacks/leetcode/next-greater/daily-temperatures-739.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(nums): 3 | n = len(nums) 4 | res = [0] * n 5 | stack = [] 6 | for i in range(n): 7 | while stack and nums[stack[-1]] < nums[i]: 8 | res[stack.pop()] = i - stack[-1] 9 | stack.append(i) 10 | return res 11 | 12 | 13 | for nums in [ 14 | [73, 74, 75, 71, 69, 72, 76, 73], 15 | [30, 40, 50, 60], 16 | [30, 60, 90] 17 | ]: 18 | print(main(nums)) 19 | -------------------------------------------------------------------------------- /stacks/leetcode/next-greater/next-greater-element-I-496.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(nums1, nums2): 3 | stack = [] 4 | map = {} 5 | for val in nums2: 6 | while stack and stack[-1] < val: 7 | map[stack.pop()] = val 8 | stack.append(val) 9 | return [map.get(val, -1) for val in nums1] 10 | 11 | 12 | for nums1, nums2 in [ 13 | ([4, 1, 2], [1, 3, 4, 2]), 14 | ([2, 4], [1, 2, 3, 4]) 15 | ]: 16 | print(main(nums1, nums2)) 17 | -------------------------------------------------------------------------------- /stacks/leetcode/next-greater/next-greater-element-II-503.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(nums): 3 | n = len(nums) 4 | res = [-1] * n 5 | stack = [] 6 | for i in range(2 * n): 7 | while stack and nums[stack[-1]] < nums[i % n]: 8 | res[stack.pop()] = nums[i % n] 9 | stack.append(i % n) 10 | return res 11 | 12 | 13 | for nums in [ 14 | [1, 2, 1], [1, 2, 3, 4, 3] 15 | ]: 16 | print(main(nums)) 17 | -------------------------------------------------------------------------------- /stacks/leetcode/online-stock-span-901.py: -------------------------------------------------------------------------------- 1 | tests = [ 2 | [100, 80, 60, 70, 60, 75, 85] 3 | ] 4 | 5 | 6 | # T=n,S=n 7 | class StockSpanner: 8 | 9 | def __init__(self): 10 | self.stack = [] 11 | self.index = 0 12 | 13 | def next(self, price): 14 | while self.stack and self.stack[-1][1] <= price: 15 | self.stack.pop() 16 | res = (self.index - self.stack[-1][0]) if self.stack else self.index + 1 17 | self.stack.append((self.index, price)) 18 | self.index += 1 19 | return res 20 | 21 | 22 | for test in tests: 23 | stock = StockSpanner() 24 | for price in test: 25 | print(stock.next(price), end=' ') 26 | print() 27 | -------------------------------------------------------------------------------- /stacks/leetcode/string/backspace-string-compare-844.py: -------------------------------------------------------------------------------- 1 | def process(s): 2 | res = [] 3 | for char in s: 4 | if char != '#': 5 | res.append(char) 6 | elif res: 7 | res.pop() 8 | return res 9 | 10 | 11 | # T=m+n,S=m+n 12 | def main(s, t): 13 | return process(s) == process(t) 14 | 15 | 16 | for s, t in [ 17 | ('ab#c', 'ad#c'), 18 | ('ab##', 'c#d#'), 19 | ('a##c', '#a#c'), 20 | ('a#c', 'b'), 21 | ('y#fo##f', 'y#f#o##f') 22 | ]: 23 | print(main(s, t)) 24 | -------------------------------------------------------------------------------- /stacks/leetcode/string/evaluate-reverse-polish-notation-150.py: -------------------------------------------------------------------------------- 1 | def compute(operator, num2, num1): 2 | if operator == '+': 3 | return num1 + num2 4 | if operator == '-': 5 | return num1 - num2 6 | if operator == '*': 7 | return num1 * num2 8 | if operator == '/': 9 | return int(num1 / num2) 10 | 11 | 12 | # T=n,S=n 13 | def main(s): 14 | stack = [] 15 | for char in s: 16 | if char not in '+-*/': 17 | stack.append(int(char)) 18 | else: 19 | stack.append(compute(char, stack.pop(), stack.pop())) 20 | return stack.pop() 21 | 22 | 23 | for s in [ 24 | ['2', '1', '+', '3', '*'], 25 | ['4', '13', '5', '/', '+'], 26 | ['10', '6', '9', '3', '+', '-11', '*', '/', '*', '17', '+', '5', '+'] 27 | ]: 28 | print(main(s)) 29 | -------------------------------------------------------------------------------- /stacks/leetcode/string/find-duplicate-parenthesis-in-an-expression.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(s): 3 | stack = [] 4 | for char in s: 5 | if char != ')': 6 | stack.append(char) 7 | continue 8 | if stack[-1] == '(': 9 | return True 10 | while stack and stack[-1] != '(': 11 | stack.pop() 12 | stack.pop() 13 | return False 14 | 15 | 16 | for s in [ 17 | '(a)', '()', '(a+b)', '((a+b))', '((a+b)+((c+d)))', '((a+b)+(c+d))' 18 | ]: 19 | print(main(s)) 20 | -------------------------------------------------------------------------------- /stacks/leetcode/string/remove-all-adjacent-duplicates-in-string-1047.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(s): 3 | res = [] 4 | for char in s: 5 | if res and res[-1] == char: 6 | res.pop() 7 | else: 8 | res.append(char) 9 | return ''.join(res) 10 | 11 | 12 | for s in [ 13 | 'abbaca', 'azxxzy' 14 | ]: 15 | print(main(s)) 16 | -------------------------------------------------------------------------------- /stacks/leetcode/string/valid-parentheses-20.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(s): 3 | stack = [] 4 | openBracketToCloseBracketMap = { 5 | '(': ')', 6 | '{': '}', 7 | '[': ']' 8 | } 9 | 10 | def isAnOpeningBracket(char): 11 | return char in openBracketToCloseBracketMap 12 | 13 | def getClosingBracket(char): 14 | return openBracketToCloseBracketMap.get(char) 15 | 16 | for char in s: 17 | if isAnOpeningBracket(char): 18 | stack.append(char) 19 | else: 20 | top = stack.pop() if stack else '' 21 | if char != getClosingBracket(top): 22 | return False 23 | return not stack 24 | 25 | 26 | for s in [ 27 | '[', 28 | '))))', 29 | '[}', 30 | '(()', 31 | '()', 32 | '()[]{}', 33 | '(]', 34 | '([)]', 35 | '{[]}', 36 | ']', 37 | '(])' 38 | ]: 39 | print(s + ' -> ' + str(main(s))) 40 | -------------------------------------------------------------------------------- /strings/leetcode/reverse-string/two-pointer/reverse-string-344.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(s): 3 | def dfs(low, high): 4 | if low < high: 5 | s[low], s[high] = s[high], s[low] 6 | dfs(low + 1, high - 1) 7 | 8 | dfs(0, len(s) - 1) 9 | 10 | 11 | for s in [ 12 | ['h', 'e', 'l', 'l', 'o'], 13 | ['h', 'a', 'n', 'n', 'a', 'h'] 14 | ]: 15 | main(s) 16 | print(s) 17 | 18 | print() 19 | 20 | 21 | # T=n,S=1 22 | def main(s): 23 | low, high = 0, len(s) - 1 24 | while low < high: 25 | s[low], s[high] = s[high], s[low] 26 | low, high = low + 1, high - 1 27 | 28 | 29 | for s in [ 30 | ['h', 'e', 'l', 'l', 'o'], 31 | ['h', 'a', 'n', 'n', 'a', 'h'] 32 | ]: 33 | main(s) 34 | print(s) 35 | -------------------------------------------------------------------------------- /strings/leetcode/reverse-words-in-a-string/two-pointer/reverse-words-in-a-string-II-186.py: -------------------------------------------------------------------------------- 1 | def reverse(s, low, high): 2 | while low < high: 3 | s[low], s[high] = s[high], s[low] 4 | low, high = low + 1, high - 1 5 | 6 | 7 | # T=n,S=1 8 | def main(s): 9 | n = len(s) 10 | start = 0 11 | for end in range(n + 1): 12 | if end == n or s[end] == ' ': 13 | reverse(s, start, end - 1) 14 | start = end + 1 15 | reverse(s, 0, n - 1) 16 | 17 | 18 | for s in [ 19 | ['t', 'h', 'e', ' ', 's', 'k', 'y', ' ', 'i', 's', ' ', 'b', 'l', 'u', 'e'], 20 | ['a'] 21 | ]: 22 | main(s) 23 | print(s) 24 | -------------------------------------------------------------------------------- /strings/leetcode/two-pointer/remove-vowels-from-a-string-1119.py: -------------------------------------------------------------------------------- 1 | inputs = [ 2 | 'leetcodeisacommunityforcoders', 3 | 'aeiou' 4 | ] 5 | 6 | 7 | def main(s): 8 | res = [] 9 | vowels = 'aeiou' 10 | for char in s: 11 | if char not in vowels: 12 | res.append(char) 13 | return ''.join(res) 14 | 15 | 16 | for s in inputs: 17 | print(main(s), end=' ') 18 | 19 | print() 20 | 21 | 22 | def main(s): 23 | return ''.join(char for char in s if char not in 'aeiou') 24 | 25 | 26 | for s in inputs: 27 | print(main(s), end=' ') 28 | -------------------------------------------------------------------------------- /strings/leetcode/two-pointer/reverse-vowels-of-a-string-345.py: -------------------------------------------------------------------------------- 1 | # T=n,S=n 2 | def main(s): 3 | n = len(s) 4 | s = list(s) 5 | low, high = 0, n - 1 6 | vowels = 'aeiouAEIOU' 7 | while low < high: 8 | while low < high and s[low] not in vowels: 9 | low += 1 10 | while low < high and s[high] not in vowels: 11 | high -= 1 12 | s[low], s[high] = s[high], s[low] 13 | low, high = low + 1, high - 1 14 | return ''.join(s) 15 | 16 | 17 | for s in [ 18 | 'xyz', 'hello', 'leetcode', 'aA', 'a.b,.', '.,' 19 | ]: 20 | print(main(s)) 21 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | def main(s): 2 | res = 0 3 | for i in range(len(s)): 4 | for j in range(i, len(s)): 5 | substring = s[i:j + 1] 6 | if len(substring) == len(set(substring)): 7 | res += 1 8 | return res 9 | 10 | 11 | print(main('abcd')) 12 | -------------------------------------------------------------------------------- /trees/binary/explore.py: -------------------------------------------------------------------------------- 1 | from heapq import heappop, heappush, heapify 2 | 3 | a = [6, 7, 4, 2, 1] 4 | 5 | heapify(a) 6 | print(heappop(a)) 7 | 8 | print(4 in [3, 4]) 9 | -------------------------------------------------------------------------------- /trees/binary/leetcode/construct/construct-binary-tree-from-inorder-and-postorder-traversal-106.py: -------------------------------------------------------------------------------- 1 | from binarytree import Node as TreeNode 2 | 3 | 4 | # T=n,S=n 5 | def main(inorder, postorder): 6 | valToIndex = {val: i for i, val in enumerate(inorder)} 7 | 8 | def dfs(low, high): 9 | if low > high: 10 | return None 11 | root = TreeNode(postorder[-1]) 12 | i = valToIndex[postorder.pop()] 13 | root.right = dfs(i + 1, high) 14 | root.left = dfs(low, i - 1) 15 | return root 16 | 17 | return dfs(0, len(inorder) - 1) 18 | 19 | 20 | for inorder, postorder in [ 21 | ([9, 3, 15, 20, 7], [9, 15, 7, 20, 3]), 22 | ([-1], [-1]), 23 | ]: 24 | print(main(inorder, postorder)) 25 | -------------------------------------------------------------------------------- /trees/binary/leetcode/construct/construct-binary-tree-from-preorder-and-inorder-traversal-105.py: -------------------------------------------------------------------------------- 1 | from binarytree import Node as TreeNode 2 | from collections import deque 3 | 4 | 5 | # T=n,S=n 6 | def main(preorder, inorder): 7 | preorder = deque(preorder) 8 | valToIndex = {val: i for i, val in enumerate(inorder)} 9 | 10 | def dfs(low, high): 11 | if low > high: 12 | return None 13 | root = TreeNode(preorder[0]) 14 | i = valToIndex[preorder.popleft()] 15 | root.left = dfs(low, i - 1) 16 | root.right = dfs(i + 1, high) 17 | return root 18 | 19 | return dfs(0, len(inorder) - 1) 20 | 21 | 22 | for preorder, inorder in [ 23 | ([3, 9, 20, 15, 7], [9, 3, 15, 20, 7]), 24 | ([0, 1, 3, 7, 8, 4, 9, 10, 2, 5, 11, 6], [7, 3, 8, 1, 9, 4, 10, 0, 11, 5, 2, 6]), 25 | ([-1], [-1]), 26 | ]: 27 | print(main(preorder, inorder)) 28 | -------------------------------------------------------------------------------- /trees/binary/leetcode/dp/binary-tree-maximum-path-sum-124.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | 4 | # T=n,S=h 5 | def main(root): 6 | res = float('-inf') 7 | 8 | def dfs(root): 9 | nonlocal res 10 | if not root: 11 | return 0 12 | maxPathSumLeft = dfs(root.left) 13 | maxPathSumRight = dfs(root.right) 14 | res = max(res, root.val, maxPathSumLeft + root.val, maxPathSumRight + root.val, 15 | maxPathSumLeft + maxPathSumRight + root.val) 16 | return max(root.val, maxPathSumLeft + root.val, maxPathSumRight + root.val) 17 | 18 | dfs(root) 19 | return res 20 | 21 | 22 | for root in [ 23 | build2([1, 2, 3]), 24 | build2([-10, 9, 20, None, None, 15, 7]), 25 | build2([2, -1]), 26 | build2([1]), 27 | build2([0]), 28 | build2([-3]), 29 | build2([9, 6, -3, None, None, -6, 2, None, None, 2, None, -6, -6, -6]) 30 | ]: 31 | print(main(root)) 32 | -------------------------------------------------------------------------------- /trees/binary/leetcode/height/maximum-depth-of-binary-tree-104.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | 4 | # T=n 5 | def dfs(root): 6 | if not root: 7 | return -1 8 | return 1 + max(dfs(root.left), dfs(root.right)) 9 | 10 | 11 | for root in [ 12 | build2([1, 2, 3, 4, 5, 6, 7, None, None, 8, None, None, 9]) 13 | ]: 14 | print(dfs(root)) 15 | -------------------------------------------------------------------------------- /trees/binary/leetcode/lca/lowest-common-ancestor-of-a-binary-tree-236.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | r1 = build2([3, 5, 1, 6, 2, 0, 8, None, None, 7, 4]) 4 | r2 = build2([3, 5, 1, 6, 2, 0, 8, None, None, 7, 4]) 5 | r3 = build2([1, 2]) 6 | inputs = [ 7 | (r1, r1.left, r1.right), 8 | (r2, r2.left, r2.left.right.right), 9 | (r3, r3, r3.left) 10 | ] 11 | 12 | 13 | # T=n,S=n 14 | def main(root, p, q): 15 | def dfs(root): 16 | if root in [None, p, q]: 17 | return root 18 | left = dfs(root.left) 19 | right = dfs(root.right) 20 | if left and right: 21 | return root 22 | return left or right 23 | 24 | return dfs(root) 25 | 26 | 27 | for root, p, q in inputs: 28 | print(main(root, p, q).val) 29 | -------------------------------------------------------------------------------- /trees/binary/leetcode/path-sum/path-sum-112.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | 4 | def main(root): 5 | def dfs(root, sum): 6 | if not root: 7 | return False 8 | sum += root.val 9 | if root.left is root.right: 10 | return sum == target 11 | return dfs(root.left, sum) or dfs(root.right, sum) 12 | 13 | return dfs(root, 0) 14 | 15 | 16 | for root, target in [ 17 | (build2([5, 4, 8, 11, None, 13, 4, 7, 2, None, None, None, 1]), 22), 18 | (build2([1, 2, 3]), 5), 19 | (build2([1, 2]), 0) 20 | ]: 21 | print(main(root)) 22 | -------------------------------------------------------------------------------- /trees/binary/leetcode/path-sum/path-sum-II-113.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | inputs = [ 4 | (build2([5, 4, 8, 11, None, 13, 4, 7, 2, None, None, 5, 1]), 22), 5 | (build2([1, 2, 3]), 5), 6 | (build2([1, 2]), 0) 7 | ] 8 | 9 | 10 | def main(root, target): 11 | def dfs(root, sum): 12 | if not root: 13 | return [] 14 | sum += root.val 15 | if root.left is root.right: 16 | return [[root.val]] if sum == target else [] 17 | res = [] 18 | for child in [root.left, root.right]: 19 | for path in dfs(child, sum): 20 | res.append([root.val] + path) 21 | return res 22 | 23 | return dfs(root, 0) 24 | 25 | 26 | for root, target in inputs: 27 | print(main(root, target)) 28 | -------------------------------------------------------------------------------- /trees/binary/leetcode/sum-root-to-leaf-numbers-129.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | 4 | # T=n,S=h 5 | def main(root): 6 | def dfs(root, num): 7 | if not root: 8 | return 0 9 | num = num * 10 + root.val 10 | if root.left is root.right: 11 | return num 12 | left = dfs(root.left, num) 13 | right = dfs(root.right, num) 14 | return left + right 15 | 16 | return dfs(root, 0) 17 | 18 | 19 | for root in [ 20 | build2([1, 2, 3]), 21 | build2([4, 9, 0, 5, 1]) 22 | ]: 23 | print(main(root)) 24 | -------------------------------------------------------------------------------- /trees/binary/leetcode/view/binary-tree-right-side-view-199.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | from collections import deque 3 | 4 | 5 | # T=n,S=n 6 | def main(root): 7 | if not root: 8 | return [] 9 | res = [] 10 | queue = deque([root]) 11 | while queue: 12 | res.append(queue[-1].val) 13 | for _ in range(len(queue)): 14 | node = queue.popleft() 15 | for child in [node.left, node.right]: 16 | if child: 17 | queue.append(child) 18 | return res 19 | 20 | 21 | for root in [ 22 | build2([1, 2, 3, None, 5, None, 4]), 23 | build2([]) 24 | ]: 25 | print(root) 26 | print(main(root)) 27 | -------------------------------------------------------------------------------- /trees/binary/practice/display.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | 4 | def main(root): 5 | def dfs(root): 6 | if not root: 7 | return 8 | print(root.val, end=' ') 9 | for child in [root.left, root.right]: 10 | dfs(child) 11 | 12 | dfs(root) 13 | 14 | 15 | for root in [ 16 | build2([50, 25, 75, 12, 37, 62, 57, None, None, 30, None, None, 70]) 17 | ]: 18 | print(root) 19 | main(root) 20 | -------------------------------------------------------------------------------- /trees/binary/practice/insert-in-level-order.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | from binarytree import build2, Node 3 | 4 | 5 | def main(root, val): 6 | if not root: 7 | return Node(val) 8 | queue = deque([root]) 9 | while queue: 10 | node = queue.popleft() 11 | if node.left: 12 | queue.append(node.left) 13 | else: 14 | node.left = Node(val) 15 | return root 16 | if node.right: 17 | queue.append(node.right) 18 | else: 19 | node.right = Node(val) 20 | return root 21 | 22 | 23 | for root in [ 24 | build2([1, 2, 3, 4, 5, 6, 7, None, None, 8, None, None, 9]) 25 | ]: 26 | print(main(root, 12)) 27 | -------------------------------------------------------------------------------- /trees/binary/practice/node-to-root-path.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | 4 | def main(root, target): 5 | def dfs(root): 6 | if not root: 7 | return [] 8 | if root.val == target: 9 | return [root.val] 10 | for child in [root.left, root.right]: 11 | path = dfs(child) 12 | if path: 13 | return path + [root.val] 14 | return [] 15 | 16 | return dfs(root) 17 | 18 | 19 | for root, target in [ 20 | (build2([1, 2, 3, 4, 5, 6, 7, None, None, 8, None, None, 9]), 10), 21 | (build2([1, 2, 3, 4, 5, 6, 7, None, None, 8, None, None, 9]), 9), 22 | (build2([1, 2, 3, 4, 5, 6, 7, None, None, 8, None, None, 9]), 7), 23 | ]: 24 | print(main(root, target)) 25 | -------------------------------------------------------------------------------- /trees/binary/practice/print-all-root-to-node-sums.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | inputs = [build2([10, 5, -3, 3, 2, None, 11, 3, -2, None, 1])] 4 | 5 | 6 | def main(root): 7 | def dfs(root, sum): 8 | if not root: 9 | return 10 | sum += root.val 11 | print(sum, end=' ') 12 | dfs(root.left, sum) 13 | dfs(root.right, sum) 14 | 15 | dfs(root, 0) 16 | 17 | 18 | for root in inputs: 19 | print(root) 20 | main(root) 21 | -------------------------------------------------------------------------------- /trees/binary/practice/root-to-leaf-max-cost-path.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | 4 | # T=n 5 | def dfs(root): 6 | if not root: 7 | return 0 8 | return root.val + max(dfs(root.left), dfs(root.right)) 9 | 10 | 11 | for root in [ 12 | build2([1, 2, 3, 4, 5, 6, 7, None, None, 8, None, None, 9]), 13 | build2([-1, -2, -3, -4, -5, -6, -7, None, None, -8, None, None, -9]), 14 | ]: 15 | print(dfs(root)) 16 | -------------------------------------------------------------------------------- /trees/binary/practice/root-to-node-path.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | 4 | def main(root, target): 5 | def dfs(root): 6 | if not root: 7 | return [] 8 | if root.val == target: 9 | return [root.val] 10 | for child in [root.left, root.right]: 11 | path = dfs(child) 12 | if path: 13 | return [root.val] + path 14 | return [] 15 | 16 | return dfs(root) 17 | 18 | 19 | for root, target in [ 20 | (build2([1, 2, 3, 4, 5, 6, 7, None, None, 8, None, None, 9]), 10), 21 | (build2([1, 2, 3, 4, 5, 6, 7, None, None, 8, None, None, 9]), 9), 22 | (build2([1, 2, 3, 4, 5, 6, 7, None, None, 8, None, None, 9]), 7), 23 | ]: 24 | print(main(root, target)) 25 | -------------------------------------------------------------------------------- /trees/bst/explore.py: -------------------------------------------------------------------------------- 1 | nums = float('inf') 2 | b = 4 3 | print(b - nums) 4 | 5 | nums = float('-inf') 6 | b = 4 7 | print(b - nums) 8 | 9 | a = [] 10 | 11 | b = ','.join((map(str, a))) 12 | 13 | print(b) 14 | 15 | print(b.split(',')) 16 | 17 | print(''.split(',')) 18 | -------------------------------------------------------------------------------- /trees/bst/leetcode/lowest-common-ancestor-of-a-binary-search-tree-235.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | r1 = build2([6, 2, 8, 0, 4, 7, 9, None, None, 3, 5]) 4 | r2 = build2([6, 2, 8, 0, 4, 7, 9, None, None, 3, 5]) 5 | r3 = build2([2, 1]) 6 | 7 | inputs = [ 8 | (r1, r1.left, r1.right), 9 | (r2, r2.left, r2.left.right.right), 10 | (r3, r3, r3.left) 11 | ] 12 | 13 | 14 | # T=n,S=n 15 | def main(root, p, q): 16 | def dfs(root): 17 | if p.val < root.val and q.val < root.val: 18 | return dfs(root.left) 19 | if p.val > root.val and q.val > root.val: 20 | return dfs(root.right) 21 | return root 22 | 23 | return dfs(root) 24 | 25 | 26 | for root, p, q in inputs: 27 | print(main(root, p, q).val) 28 | -------------------------------------------------------------------------------- /trees/bst/leetcode/range/construct-binary-search-tree-from-preorder-traversal-1008.py: -------------------------------------------------------------------------------- 1 | from binarytree import Node as TreeNode 2 | from collections import deque 3 | 4 | 5 | # T=n,S=n 6 | def main(preorder): 7 | queue = deque(preorder) 8 | 9 | def dfs(low, high): 10 | if not queue or queue[0] < low or queue[0] > high: 11 | return None 12 | val = queue.popleft() 13 | root = TreeNode(val) 14 | root.left = dfs(low, val) 15 | root.right = dfs(val, high) 16 | return root 17 | 18 | return dfs(float('-inf'), float('inf')) 19 | 20 | 21 | for preorder in [ 22 | [8, 5, 1, 7, 10, 12], 23 | [1, 3] 24 | ]: 25 | print(main(preorder)) 26 | -------------------------------------------------------------------------------- /trees/bst/leetcode/range/validate-binary-search-tree-98.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | 4 | # T=n 5 | def main(root): 6 | def dfs(root, low, high): 7 | if not root: 8 | return True 9 | left = dfs(root.left, low, root.val) 10 | right = dfs(root.right, root.val, high) 11 | return low < root.val < high and left and right 12 | 13 | return dfs(root, float('-inf'), float('inf')) 14 | 15 | 16 | for root in [ 17 | build2([2, 1, 3]), 18 | build2([5, 1, 4, None, None, 3, 6]), 19 | build2([50, 25, 75, 12, 37, 62, 87, None, None, 30, None, None, 70, None, None]), 20 | build2([1, 2]), 21 | build2([2, 1]) 22 | ]: 23 | print(root) 24 | print(main(root)) 25 | -------------------------------------------------------------------------------- /trees/bst/leetcode/search-in-a-binary-search-tree-700.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | inputs = [ 4 | (build2([4, 2, 7, 1, 3]), 2), 5 | (build2([4, 2, 7, 1, 3]), 5) 6 | ] 7 | 8 | 9 | # T=h 10 | def main(root, target): 11 | def dfs(root): 12 | if not root or target == root.val: 13 | return root 14 | if target > root.val: 15 | return dfs(root.right) 16 | return dfs(root.left) 17 | 18 | return dfs(root) 19 | 20 | 21 | for root, target in inputs: 22 | print(main(root, target), end=' ') 23 | 24 | print() 25 | 26 | 27 | # T=h 28 | def main(root, target): 29 | while root: 30 | if target == root.val: 31 | return root 32 | if target > root.val: 33 | root = root.right 34 | else: 35 | root = root.left 36 | return root 37 | 38 | 39 | for root, target in inputs: 40 | print(main(root, target), end=' ') 41 | -------------------------------------------------------------------------------- /trees/bst/leetcode/unique/unique-binary-search-trees-95.py: -------------------------------------------------------------------------------- 1 | from binarytree import Node as TreeNode 2 | 3 | 4 | # T=n²,S=n 5 | def main(n): 6 | cache = {0: 1} 7 | 8 | def dfs(n): 9 | if n in cache: 10 | return cache[n] 11 | cache[n] = 0 12 | for i in range(n): 13 | cache[n] += dfs(i) * dfs(n - 1 - i) 14 | return cache[n] 15 | 16 | return dfs(n) 17 | 18 | 19 | for n in [ 20 | 1, 2, 3, 4, 19 21 | ]: 22 | print(main(n)) 23 | -------------------------------------------------------------------------------- /trees/bst/leetcode/unique/unique-binary-search-trees-II-96.py: -------------------------------------------------------------------------------- 1 | from binarytree import Node as TreeNode 2 | 3 | 4 | def main(n): 5 | def dfs(start, end): 6 | if start > end: 7 | return [None] 8 | res = [] 9 | for i in range(start, end + 1): 10 | left = dfs(start, i - 1) 11 | right = dfs(i + 1, end) 12 | for lst in left: 13 | for rst in right: 14 | res.append(TreeNode(i, lst, rst)) 15 | return res 16 | 17 | return dfs(1, n) 18 | 19 | 20 | for n in [ 21 | 1, 3 22 | ]: 23 | for root in main(n): 24 | print(root) 25 | -------------------------------------------------------------------------------- /trees/bst/search.py: -------------------------------------------------------------------------------- 1 | from binarytree import build2 2 | 3 | inputs = [ 4 | (build2([4, 2, 7, 1, 3]), 2), 5 | (build2([4, 2, 7, 1, 3]), 5) 6 | ] 7 | 8 | 9 | # T=h 10 | def main(root, target): 11 | def dfs(root): 12 | if not root: 13 | return False 14 | if target == root.val: 15 | return True 16 | if target > root.val: 17 | return dfs(root.right) 18 | return dfs(root.left) 19 | 20 | return dfs(root) 21 | 22 | 23 | for root, target in inputs: 24 | print(main(root, target)) 25 | 26 | print() 27 | 28 | 29 | # T=h 30 | def main(root, target): 31 | while root: 32 | if target == root.val: 33 | return True 34 | if target > root.val: 35 | root = root.right 36 | else: 37 | root = root.left 38 | return False 39 | 40 | 41 | for root, target in inputs: 42 | print(main(root, target)) 43 | -------------------------------------------------------------------------------- /warmup/leetcode/math/number-of-steps-to-reduce-a-number-to-zero-1342.py: -------------------------------------------------------------------------------- 1 | # T=logn,S=1 2 | def x(num): 3 | def dfs(num): 4 | if not num: 5 | return 0 6 | if num % 2: 7 | return 1 + dfs(num - 1) 8 | return 1 + dfs(num // 2) 9 | 10 | return dfs(num) 11 | 12 | 13 | # T=logn,S=1 14 | def y(num): 15 | res = 0 16 | while num: 17 | if num % 2: 18 | num -= 1 19 | else: 20 | num //= 2 21 | res += 1 22 | return res 23 | 24 | 25 | for num in [ 26 | 14, 8, 123 27 | ]: 28 | print(x(num), end=' ') 29 | print(y(num)) 30 | -------------------------------------------------------------------------------- /warmup/leetcode/math/reverse-integer-7.py: -------------------------------------------------------------------------------- 1 | # T=logn,S=1 2 | def main(n): 3 | res = 0 4 | factor = -1 if n < 0 else 1 5 | range = 2 ** 31 6 | n = abs(n) 7 | while n: 8 | res = 10 * res + n % 10 9 | n //= 10 10 | if res < -range or res > range: 11 | return 0 12 | return factor * res 13 | 14 | 15 | for n in [ 16 | 123, -123, 120, 0, 1534236469 17 | ]: 18 | print(main(n)) 19 | -------------------------------------------------------------------------------- /warmup/leetcode/robot-bounded-in-circle-1041.py: -------------------------------------------------------------------------------- 1 | # T=n,S=1 2 | def main(instructions): 3 | directions = [[0, 1], [1, 0], [0, -1], [-1, 0]] 4 | i = x = y = 0 5 | for instruction in instructions: 6 | if instruction == 'L': 7 | i = (i + 3) % 4 8 | elif instruction == 'R': 9 | i = (i + 1) % 4 10 | else: 11 | x += directions[i][0] 12 | y += directions[i][1] 13 | return (x, y) == (0, 0) or i != 0 14 | 15 | 16 | for instructions in [ 17 | 'GGLLGG', 18 | 'GG' 19 | ]: 20 | print(main(instructions)) 21 | -------------------------------------------------------------------------------- /warmup/leetcode/roman/integer-to-roman-12.py: -------------------------------------------------------------------------------- 1 | # T=1,S=1 2 | def main(num): 3 | res, i = [], 0 4 | digits = [ 5 | ('M', 1000), ('CM', 900), ('D', 500), ('CD', 400), ('C', 100), ('XC', 90), ('L', 50), ('XL', 40), ('X', 10), 6 | ('IX', 9), ('V', 5), ('IV', 4), ('I', 1)] 7 | while num: 8 | symbol, val = digits[i] 9 | count, num = divmod(num, val) 10 | res.append(count * symbol) 11 | i += 1 12 | return ''.join(res) 13 | 14 | 15 | for num in [ 16 | 3, 4, 9, 58, 478, 1994, 671, 3888, 3999 17 | ]: 18 | print(main(num)) 19 | --------------------------------------------------------------------------------