├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── LICENSE ├── README.md ├── nest-cli.json ├── package-lock.json ├── package.json ├── problem_tables ├── Array.csv ├── Backtracking.csv ├── Binary_Indexed_Tree.csv ├── Binary_Lifting.csv ├── Binary_Search.csv ├── Binary_Search_Tree.csv ├── Binary_Tree.csv ├── Bit_Manipulation.csv ├── Breadth-First_Search.csv ├── Concurrency.csv ├── Counting.csv ├── Date.csv ├── Depth-First_Search.csv ├── Deque.csv ├── Design.csv ├── Dijkstra's_Algorithm.csv ├── Divide_and_Conquer.csv ├── Dynamic_Programming.csv ├── Game_Theory.csv ├── Geometry.csv ├── Graph.csv ├── Greedy.csv ├── Hash_Map.csv ├── Hash_Set.csv ├── Hash_Table.csv ├── Hashing.csv ├── Heap.csv ├── Heap_(Priority_Queue).csv ├── Interactive.csv ├── Linked_List.csv ├── Math.csv ├── Matrix.csv ├── Memoization.csv ├── Minimax.csv ├── Parsing.csv ├── Prefix_Sum.csv ├── Priority_Queue.csv ├── Probability.csv ├── Queue.csv ├── Recursion.csv ├── Rolling_Hash.csv ├── SQL.csv ├── Segment_Tree.csv ├── Shell.csv ├── Simulation.csv ├── Sliding_Window.csv ├── Sorting.csv ├── Stack.csv ├── String.csv ├── Topological_Sort.csv ├── Tree.csv ├── Trie.csv ├── Two_Pointers.csv └── Union_Find.csv ├── src ├── array │ ├── binary_search │ │ ├── find-closest-elements.ts │ │ ├── longest-Increasing-subseq.ts │ │ └── search-in-rotated-sorted-array.ts │ ├── bs │ │ ├── koko-eating-bananas.ts │ │ └── search-2D-Matrix-II.ts │ ├── count-primes.ts │ ├── dp │ │ └── num-squares.ts │ ├── hash_table │ │ ├── intersection.ts │ │ ├── intersectionII.ts │ │ ├── is-reflected.ts │ │ └── two-sum.ts │ ├── intervals │ │ ├── erase-overlap-intervals.ts │ │ ├── insert-interval.ts │ │ ├── interval-intersection.ts │ │ ├── merge-intervals.ts │ │ ├── min-groups.ts │ │ ├── min-meeting-rooms.ts │ │ ├── remove-covered-intervals.ts │ │ ├── remove-interval.ts │ │ └── summary-ranges.ts │ ├── is-palindrome.ts │ ├── majority-element.ts │ ├── matrix │ │ ├── equal-pairs.ts │ │ ├── generate-matrix.ts │ │ └── rotate-matrix.ts │ ├── max-consecutive-ones.ts │ ├── max-profit.ts │ ├── prefix_sum │ │ ├── prefix-common-common-array.ts │ │ ├── product-except-self.ts │ │ ├── subarray-sum.ts │ │ └── subarrays-div-by-k.ts │ ├── sliding_window │ │ ├── find-max-average.ts │ │ ├── longest-subarray.ts │ │ ├── max-consecutive-onesII.ts │ │ ├── max-consecutive-onesIII.ts │ │ └── max-dist-to-closet.ts │ ├── sorting │ │ ├── diagonal-sort.ts │ │ └── merge-sorted-array.ts │ └── two_pointers │ │ ├── 3sum-closest.ts │ │ ├── 3sum.ts │ │ ├── find-closest-elements.ts │ │ ├── intersection.ts │ │ ├── move-zeros.ts │ │ ├── remove-zeros.ts │ │ ├── reverse-vowels.ts │ │ ├── sorted-squares.ts │ │ ├── trapping-rain-water.ts │ │ └── two-sum.ts ├── bst │ ├── bfs-all-paths.task.ts │ ├── bst-max-path-sum.ts │ ├── bst-side-view.ts │ ├── delete.ts │ ├── duplicates-subtrees.ts │ ├── find-min.ts │ ├── insert.ts │ ├── is-balanced.ts │ ├── is-symmetric.ts │ ├── is-valid-bst.ts │ ├── leaf-similar.ts │ ├── lowest-common-ancestor.ts │ ├── max-depth.ts │ ├── serialize-and-deserialize.ts │ ├── sum-of-left-leaves.ts │ └── utilities │ │ ├── bst.structure.ts │ │ ├── bst.utilities.ts │ │ └── index.ts ├── graph │ ├── alien-order.ts │ ├── calc-equation.ts │ ├── critical-connections.ts │ ├── find-iternarary.ts │ ├── min-malware-spread.ts │ ├── network-delay-time.ts │ ├── num-islands.ts │ ├── utilities │ │ ├── graph.structure.ts │ │ ├── graph.utilities.ts │ │ └── index.ts │ └── web-crawler.ts ├── heap │ ├── find-relative-ranks.ts │ ├── max-sliding-window.ts │ └── utilities │ │ ├── heap.test.ts │ │ └── heap.ts ├── js_core │ ├── emiter-to-iterator.ts │ ├── flatten.ts │ └── promise-pool.ts ├── linked_list │ ├── add-two-numbers.ts │ ├── delete-duplicates.ts │ ├── delete-middle.ts │ ├── linked_list.structure.ts │ ├── merge-k-lists.ts │ ├── merge-two-lists.ts │ ├── odd-even-list.ts │ ├── remove-nth-fromend.ts │ ├── reverse-list.ts │ ├── split-list-to-parts.ts │ └── utilities.ts ├── main.ts ├── multi-thread │ ├── base-pool.ts │ └── worker │ │ └── worker.ts ├── queue │ └── predict-party-victory.ts ├── recursion │ ├── generate-parenthesis.ts │ └── problems.ts ├── scratch_structure │ ├── atm.ts │ ├── event-emitter.ts │ ├── hit-counter.ts │ ├── lru-cache.ts │ ├── min-stack.ts │ ├── nested-iterator.ts │ ├── randomized-set.ts │ ├── rate-limiter.ts │ ├── recent-counter.ts │ ├── stack-queue.ts │ ├── unique-structure.ts │ └── zigzag-iterator.ts ├── sort │ ├── bubble.sort.ts │ ├── bucket.sort.ts │ ├── cocktail.sort.ts │ ├── comb.sort.ts │ ├── counting.sort.ts │ ├── heap.sort.ts │ ├── index.ts │ ├── insertion.sort.ts │ ├── merge.sort.ts │ ├── quick.sort.ts │ ├── radix.sort.ts │ ├── selection.sort.ts │ ├── shell.sort.ts │ ├── sort.d.ts │ ├── tim.sort.ts │ └── utilities.ts ├── stack │ ├── asteroid-collision.ts │ ├── decode-string.ts │ ├── eval-rpn.ts │ ├── remove-stars.ts │ ├── simplify-path.ts │ └── valid-parentheses.ts ├── string │ ├── binary_search │ │ └── next-greatest-letter.ts │ ├── compress.ts │ ├── counting │ │ ├── check-inclusion.ts │ │ └── close-string.ts │ ├── find-index-first-occurance.ts │ ├── find-panagrams.string.ts │ ├── group-anagrams.ts │ ├── hash_table │ │ └── is-isomorfic.ts │ ├── is-one-edit-distance.ts │ ├── is-palindrome.ts │ ├── partition-labels.ts │ ├── remove-invalid-parentheses.ts │ ├── robot-return-to-origin.ts │ ├── sliding_window │ │ ├── find-anagrams.ts │ │ └── longest-repeating-character-replacement.ts │ ├── substring │ │ ├── longest-substring-k-distinct.ts │ │ ├── longest-substring.ts │ │ ├── min-window-substring.ts │ │ └── repeated-substring-pattern.ts │ └── two_pointers │ │ ├── check-inclusion.ts │ │ ├── is-subsequence.ts │ │ ├── longest-palindrome.ts │ │ └── valid-palindrome.ts ├── templates │ └── problem.template.ts └── typescript │ └── dot-path.ts ├── test └── test.ts ├── tsconfig.build.json ├── tsconfig.json └── unique_tasks.csv /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Launch Program", 8 | "skipFiles": [ "/**" ], 9 | "program": "${workspaceFolder}/src/main.ts", 10 | "cwd": "${workspaceFolder}", 11 | "console": "integratedTerminal", 12 | "preLaunchTask": "build", 13 | "envFile": "${workspaceFolder}/.env", 14 | "sourceMaps": true 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "build", 7 | "group": "build", 8 | "problemMatcher": [], 9 | "label": "build", 10 | "detail": "tsc --build" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs_algorithms", 3 | "version": "1.0.0", 4 | "main": "main.js", 5 | "scripts": { 6 | "test": "jest", 7 | "build": "tsc --build" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "description": "", 12 | "devDependencies": { 13 | "@faker-js/faker": "^8.4.1", 14 | "@types/jest": "^29.5.12", 15 | "jest": "^29.7.0", 16 | "ts-jest": "^29.2.5", 17 | "typescript": "^5.5.4" 18 | }, 19 | "jest": { 20 | "preset": "ts-jest", 21 | "testEnvironment": "node", 22 | "moduleFileExtensions": [ 23 | "ts", 24 | "tsx", 25 | "js" 26 | ], 27 | "testMatch": [ 28 | "**/__tests__/**/*.+(ts|tsx)", 29 | "**/?(*.)+(spec|test).+(ts|tsx)" 30 | ], 31 | "transform": { 32 | "^.+\\.(ts|tsx)$": "ts-jest" 33 | }, 34 | "testPathIgnorePatterns": [ 35 | "/node_modules/", 36 | "/dist/" 37 | ] 38 | }, 39 | "dependencies": { 40 | "@types/cli-progress": "^3.11.6", 41 | "@types/workerpool": "^6.4.7", 42 | "cli-progress": "^3.12.0", 43 | "piscina": "^4.9.2", 44 | "reflect-metadata": "^0.2.2", 45 | "workerpool": "^9.1.3" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /problem_tables/Binary_Indexed_Tree.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1157,Online Majority Element In Subarray,Hard,https://leetcode.com/problems/online-majority-element-in-subarray,"Segment Tree, Binary Indexed Tree, Binary Search", 3 | 327,Count of Range Sum,Hard,https://leetcode.com/problems/count-of-range-sum,"Divide and Conquer, Binary Search Tree, Binary Indexed Tree", 4 | -------------------------------------------------------------------------------- /problem_tables/Binary_Lifting.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1483,Kth Ancestor of a Tree Node,Hard,https://leetcode.com/problems/kth-ancestor-of-a-tree-node,"Tree, Binary Lifting", 3 | -------------------------------------------------------------------------------- /problem_tables/Binary_Search_Tree.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 327,Count of Range Sum,Hard,https://leetcode.com/problems/count-of-range-sum,"Divide and Conquer, Binary Search Tree, Binary Indexed Tree", 3 | -------------------------------------------------------------------------------- /problem_tables/Binary_Tree.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1530,Number of Good Leaf Nodes Pairs,Medium,https://leetcode.com/problems/number-of-good-leaf-nodes-pairs,"Tree, Depth-First Search, Binary Tree", 3 | -------------------------------------------------------------------------------- /problem_tables/Concurrency.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1114,Print in Order,Easy, https://leetcode.com/problems/print-in-order," ""Concurrency""", 3 | 1279,Traffic Light Controlled Intersection,Easy, https://leetcode.com/problems/traffic-light-controlled-intersection," ""Concurrency""", 4 | 1188,Design Bounded Blocking Queue,Medium, https://leetcode.com/problems/design-bounded-blocking-queue," ""Concurrency""", 5 | 1242,Web Crawler Multithreaded,Medium, https://leetcode.com/problems/web-crawler-multithreaded," ""Concurrency","[' Depth-First Search', ' Breadth-First Search""']" 6 | 1117,Building H2O,Medium, https://leetcode.com/problems/building-h2o," ""Concurrency""", 7 | 1116,Print Zero Even Odd,Medium, https://leetcode.com/problems/print-zero-even-odd," ""Concurrency""", 8 | -------------------------------------------------------------------------------- /problem_tables/Counting.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1121,Divide Array Into Increasing Sequences,Hard,https://leetcode.com/problems/divide-array-into-increasing-sequences,"Greedy, Counting", 3 | -------------------------------------------------------------------------------- /problem_tables/Date.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1154,Day of the Year,Easy,https://leetcode.com/problems/day-of-the-year,Date, 3 | -------------------------------------------------------------------------------- /problem_tables/Deque.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1499,Max Value of Equation,Hard,https://leetcode.com/problems/max-value-of-equation,"Sliding Window, Priority Queue, Deque", 3 | -------------------------------------------------------------------------------- /problem_tables/Dijkstra's_Algorithm.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1334,Find the City With the Smallest Number of Neighbors at a Threshold Distance,Medium,https://leetcode.com/problems/find-the-city-with-the-smallest-number-of-neighbors-at-a-threshold-distance,"Graph, Dijkstra's Algorithm", 3 | -------------------------------------------------------------------------------- /problem_tables/Divide_and_Conquer.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 218,The Skyline Problem,Hard, https://leetcode.com/problems/the-skyline-problem," ""Divide and Conquer","[' Heap (Priority Queue)', ' Segment Tree""']" 3 | 395,Longest Substring with At Least K Repeating Characters,Medium, https://leetcode.com/problems/longest-substring-with-at-least-k-repeating-characters," ""Divide and Conquer","[' Sliding Window""']" 4 | 932,Beautiful Array,Medium,https://leetcode.com/problems/beautiful-array,"Math, Divide and Conquer", 5 | 327,Count of Range Sum,Hard,https://leetcode.com/problems/count-of-range-sum,"Divide and Conquer, Binary Search Tree, Binary Indexed Tree", 6 | -------------------------------------------------------------------------------- /problem_tables/Game_Theory.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1406,Stone Game III,Hard,https://leetcode.com/problems/stone-game-iii,"Dynamic Programming, Game Theory", 3 | -------------------------------------------------------------------------------- /problem_tables/Geometry.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 836,Rectangle Overlap,Easy, https://leetcode.com/problems/rectangle-overlap," ""Geometry""", 3 | 1515,Best Position for a Service Centre,Hard, https://leetcode.com/problems/best-position-for-a-service-centre," ""Geometry","[' Math""']" 4 | 1453,Maximum Number of Darts Inside of a Circular Dartboard,Hard, https://leetcode.com/problems/maximum-number-of-darts-inside-of-a-circular-dartboard," ""Geometry","[' Math""']" 5 | 850,Rectangle Area II,Hard, https://leetcode.com/problems/rectangle-area-ii," ""Geometry","[' Segment Tree', ' Line Sweep""']" 6 | 469,Convex Polygon,Medium,https://leetcode.com/problems/convex-polygon,"Geometry, Math", 7 | 587,Erect the Fence,Hard,https://leetcode.com/problems/erect-the-fence,"Geometry, Math", 8 | 812,Largest Triangle Area,Easy,https://leetcode.com/problems/largest-triangle-area,"Math, Geometry", 9 | 1037,Valid Boomerang,Easy,https://leetcode.com/problems/valid-boomerang,Geometry, 10 | 1401,Circle and Rectangle Overlapping,Medium,https://leetcode.com/problems/circle-and-rectangle-overlapping,"Geometry, Math", 11 | 1232,Check If It Is a Straight Line,Easy,https://leetcode.com/problems/check-if-it-is-a-straight-line,"Geometry, Math", 12 | 1459,Rectangles Area,Medium,https://leetcode.com/problems/rectangles-area,"Geometry, Math", 13 | 1039,Minimum Score Triangulation of Polygon,Medium,https://leetcode.com/problems/minimum-score-triangulation-of-polygon,"Dynamic Programming, Geometry", 14 | -------------------------------------------------------------------------------- /problem_tables/Hash_Map.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1488,Avoid Flood in The City,Medium,https://leetcode.com/problems/avoid-flood-in-the-city,"Greedy, Hash Map", 3 | 1546,Maximum Number of Non-Overlapping Subarrays With Sum Equals Target,Medium,https://leetcode.com/problems/maximum-number-of-non-overlapping-subarrays-with-sum-equals-target,"Greedy, Hash Map", 4 | 170,Two Sum III - Data structure design,Easy,https://leetcode.com/problems/two-sum-iii-data-structure-design,"Hash Map, Design", 5 | 599,Minimum Index Sum of Two Lists,Easy,https://leetcode.com/problems/minimum-index-sum-of-two-lists,Hash Map, 6 | 1497,Check If Array Pairs Are Divisible by k,Medium,https://leetcode.com/problems/check-if-array-pairs-are-divisible-by-k,"Hash Map, Math", 7 | 1282,Group the People Given the Group Size They Belong To,Medium,https://leetcode.com/problems/group-the-people-given-the-group-size-they-belong-to,Hash Map, 8 | 1189,Maximum Number of Balloons,Easy,https://leetcode.com/problems/maximum-number-of-balloons,"Hash Map, String", 9 | 1500,Design a File Sharing System,Medium,https://leetcode.com/problems/design-a-file-sharing-system,"Design, Hash Map", 10 | 1348,Tweet Counts Per Frequency,Medium,https://leetcode.com/problems/tweet-counts-per-frequency,"Design, Hash Map", 11 | 648,Replace Words,Medium,https://leetcode.com/problems/replace-words,"Trie, Hash Map", 12 | 1400,Construct K Palindrome Strings,Medium,https://leetcode.com/problems/construct-k-palindrome-strings,"Greedy, Hash Map", 13 | 1436,Destination City,Easy,https://leetcode.com/problems/destination-city,"Graph, Hash Map", 14 | -------------------------------------------------------------------------------- /problem_tables/Hash_Set.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1461,Check If a String Contains All Binary Codes of Size K,Medium,https://leetcode.com/problems/check-if-a-string-contains-all-binary-codes-of-size-k,"String, Hash Set, Sliding Window", 3 | 1452,People Whose List of Favorite Companies Is Not a Subset of Another List,Medium,https://leetcode.com/problems/people-whose-list-of-favorite-companies-is-not-a-subset-of-another-list,"Hash Set, Sorting", 4 | 1525,Number of Good Ways to Split a String,Medium,https://leetcode.com/problems/number-of-good-ways-to-split-a-string,"String, Hash Set", 5 | 575,Distribute Candies,Easy,https://leetcode.com/problems/distribute-candies,Hash Set, 6 | -------------------------------------------------------------------------------- /problem_tables/Hashing.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1316,Distinct Echo Substrings,Hard,https://leetcode.com/problems/distinct-echo-substrings,"String, Hashing", 3 | -------------------------------------------------------------------------------- /problem_tables/Heap.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 703,Kth Largest Element in a Stream,Easy, https://leetcode.com/problems/kth-largest-element-in-a-stream," ""Heap","[' Design""']" 3 | 295,Find Median from Data Stream,Hard, https://leetcode.com/problems/find-median-from-data-stream," ""Heap","[' Design', ' Data Stream""']" 4 | 313,Super Ugly Number,Medium,https://leetcode.com/problems/super-ugly-number,"Heap, Math", 5 | 656,Coin Path,Hard,https://leetcode.com/problems/coin-path,"Dynamic Programming, Heap", 6 | 786,K-th Smallest Prime Fraction,Hard,https://leetcode.com/problems/k-th-smallest-prime-fraction,"Heap, Binary Search", 7 | 1354,Construct Target Array With Multiple Sums,Hard,https://leetcode.com/problems/construct-target-array-with-multiple-sums,"Heap, Greedy", 8 | -------------------------------------------------------------------------------- /problem_tables/Heap_(Priority_Queue).csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1046,Last Stone Weight,Easy, https://leetcode.com/problems/last-stone-weight," ""Heap (Priority Queue)""", 3 | 659,Split Array into Consecutive Subsequences,Medium, https://leetcode.com/problems/split-array-into-consecutive-subsequences," ""Heap (Priority Queue)","[' Greedy', ' Hash Table""']" 4 | 630,Course Schedule III,Hard,https://leetcode.com/problems/course-schedule-iii,"Greedy, Sorting, Heap (Priority Queue)", 5 | -------------------------------------------------------------------------------- /problem_tables/Interactive.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1538,Guess the Majority in a Hidden Array,Medium,https://leetcode.com/problems/guess-the-majority-in-a-hidden-array,"Array, Interactive", 3 | -------------------------------------------------------------------------------- /problem_tables/Matrix.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1444,Number of Ways of Cutting a Pizza,Hard,https://leetcode.com/problems/number-of-ways-of-cutting-a-pizza,"Dynamic Programming, Matrix", 3 | 1314,Matrix Block Sum,Medium,https://leetcode.com/problems/matrix-block-sum,"Array, Matrix", 4 | 861,Score After Flipping Matrix,Medium,https://leetcode.com/problems/score-after-flipping-matrix,"Greedy, Matrix", 5 | 1210,Minimum Moves to Reach Target with Rotations,Hard,https://leetcode.com/problems/minimum-moves-to-reach-target-with-rotations,"Breadth-First Search, Matrix", 6 | 1222,Queens That Can Attack the King,Medium,https://leetcode.com/problems/queens-that-can-attack-the-king,"Array, Matrix, Simulation", 7 | 1380,Lucky Numbers in a Matrix,Easy,https://leetcode.com/problems/lucky-numbers-in-a-matrix,"Array, Matrix", 8 | 1329,Sort the Matrix Diagonally,Medium,https://leetcode.com/problems/sort-the-matrix-diagonally,"Matrix, Sorting", 9 | -------------------------------------------------------------------------------- /problem_tables/Memoization.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 464,Can I Win,Medium,https://leetcode.com/problems/can-i-win,"Dynamic Programming, Memoization", 3 | -------------------------------------------------------------------------------- /problem_tables/Minimax.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 913,Cat and Mouse,Hard,https://leetcode.com/problems/cat-and-mouse,"Graph, Dynamic Programming, Minimax", 3 | -------------------------------------------------------------------------------- /problem_tables/Parsing.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1507,Reformat Date,Easy,https://leetcode.com/problems/reformat-date,"String, Parsing", 3 | -------------------------------------------------------------------------------- /problem_tables/Prefix_Sum.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1124,Longest Well-Performing Interval,Medium,https://leetcode.com/problems/longest-well-performing-interval,"Array, Stack, Prefix Sum", 3 | 1171,Remove Zero Sum Consecutive Nodes from Linked List,Medium,https://leetcode.com/problems/remove-zero-sum-consecutive-nodes-from-linked-list,"Linked List, Prefix Sum", 4 | -------------------------------------------------------------------------------- /problem_tables/Priority_Queue.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 499,The Maze III,Hard,https://leetcode.com/problems/the-maze-iii,"Graph, Breadth-First Search, Priority Queue", 3 | 1199,Minimum Time to Build Blocks,Hard,https://leetcode.com/problems/minimum-time-to-build-blocks,"Greedy, Priority Queue", 4 | 1514,Path with Maximum Probability,Medium,https://leetcode.com/problems/path-with-maximum-probability,"Graph, Priority Queue", 5 | 1499,Max Value of Equation,Hard,https://leetcode.com/problems/max-value-of-equation,"Sliding Window, Priority Queue, Deque", 6 | -------------------------------------------------------------------------------- /problem_tables/Probability.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1230,Toss Strange Coins,Medium,https://leetcode.com/problems/toss-strange-coins,"Dynamic Programming, Probability", 3 | -------------------------------------------------------------------------------- /problem_tables/Queue.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 346,Moving Average from Data Stream,Easy, https://leetcode.com/problems/moving-average-from-data-stream," ""Queue","[' Design""']" 3 | 933,Number of Recent Calls,Easy, https://leetcode.com/problems/number-of-recent-calls," ""Queue","[' Design""']" 4 | 649,Dota2 Senate,Medium,https://leetcode.com/problems/dota2-senate,"Greedy, Queue", 5 | -------------------------------------------------------------------------------- /problem_tables/Recursion.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 273,Integer to English Words,Hard, https://leetcode.com/problems/integer-to-english-words," ""Recursion","[' String""']" 3 | 241,Different Ways to Add Parentheses,Medium, https://leetcode.com/problems/different-ways-to-add-parentheses," ""Recursion","[' Dynamic Programming', ' Divide and Conquer""']" 4 | 247,Strobogrammatic Number II,Medium, https://leetcode.com/problems/strobogrammatic-number-ii," ""Recursion""", 5 | 761,Special Binary String,Hard, https://leetcode.com/problems/special-binary-string," ""Recursion","[' Greedy', ' Divide and Conquer""']" 6 | 1265,Print Immutable Linked List in Reverse,Medium,https://leetcode.com/problems/print-immutable-linked-list-in-reverse,"Linked List, Stack, Recursion", 7 | 544,Output Contest Matches,Medium,https://leetcode.com/problems/output-contest-matches,Recursion, 8 | 156,Binary Tree Upside Down,Medium,https://leetcode.com/problems/binary-tree-upside-down,"Tree, Recursion", 9 | -------------------------------------------------------------------------------- /problem_tables/Rolling_Hash.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1392,Longest Happy Prefix,Hard,https://leetcode.com/problems/longest-happy-prefix,"String, Rolling Hash", 3 | -------------------------------------------------------------------------------- /problem_tables/Segment_Tree.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1157,Online Majority Element In Subarray,Hard,https://leetcode.com/problems/online-majority-element-in-subarray,"Segment Tree, Binary Indexed Tree, Binary Search", 3 | -------------------------------------------------------------------------------- /problem_tables/Shell.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 192,Word Frequency,Medium, https://leetcode.com/problems/word-frequency," ""Shell""", 3 | 195,Tenth Line,Easy, https://leetcode.com/problems/tenth-line," ""Shell""", 4 | 193,Valid Phone Numbers,Easy, https://leetcode.com/problems/valid-phone-numbers," ""Shell""", 5 | -------------------------------------------------------------------------------- /problem_tables/Simulation.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 950,Reveal Cards In Increasing Order,Medium,https://leetcode.com/problems/reveal-cards-in-increasing-order,"Array, Simulation", 3 | 1441,Build an Array With Stack Operations,Easy,https://leetcode.com/problems/build-an-array-with-stack-operations,"Array, Stack, Simulation", 4 | 1503,Last Moment Before All Ants Fall Out of a Plank,Medium,https://leetcode.com/problems/last-moment-before-all-ants-fall-out-of-a-plank,"Math, Simulation", 5 | 874,Walking Robot Simulation,Easy,https://leetcode.com/problems/walking-robot-simulation,"Array, Simulation", 6 | 1222,Queens That Can Attack the King,Medium,https://leetcode.com/problems/queens-that-can-attack-the-king,"Array, Matrix, Simulation", 7 | 1006,Clumsy Factorial,Medium,https://leetcode.com/problems/clumsy-factorial,"Math, Simulation", 8 | 1518,Water Bottles,Easy,https://leetcode.com/problems/water-bottles,"Greedy, Simulation", 9 | 495,Teemo Attacking,Medium,https://leetcode.com/problems/teemo-attacking,"Array, Simulation", 10 | 999,Available Captures for Rook,Easy,https://leetcode.com/problems/available-captures-for-rook,"Array, Simulation", 11 | 1389,Create Target Array in the Given Order,Easy,https://leetcode.com/problems/create-target-array-in-the-given-order,"Array, Simulation", 12 | 1204,Last Person to Fit in the Elevator,Medium,https://leetcode.com/problems/last-person-to-fit-in-the-elevator,Simulation, 13 | -------------------------------------------------------------------------------- /problem_tables/Sliding_Window.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1234,Replace the Substring for Balanced String,Medium, https://leetcode.com/problems/replace-the-substring-for-balanced-string," ""Sliding Window","[' Two Pointers', ' String', ' Hash Table""']" 3 | 643,Maximum Average Subarray I,Easy,https://leetcode.com/problems/maximum-average-subarray-i,"Array, Sliding Window", 4 | 1461,Check If a String Contains All Binary Codes of Size K,Medium,https://leetcode.com/problems/check-if-a-string-contains-all-binary-codes-of-size-k,"String, Hash Set, Sliding Window", 5 | 1499,Max Value of Equation,Hard,https://leetcode.com/problems/max-value-of-equation,"Sliding Window, Priority Queue, Deque", 6 | 1343,Number of Sub-arrays of Size K and Average Greater than or Equal to Threshold,Medium,https://leetcode.com/problems/number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold,"Array, Sliding Window", 7 | 1156,Swap For Longest Repeated Character Substring,Medium,https://leetcode.com/problems/swap-for-longest-repeated-character-substring,"Two Pointers, Sliding Window", 8 | 1052,Grumpy Bookstore Owner,Medium,https://leetcode.com/problems/grumpy-bookstore-owner,Sliding Window, 9 | 1208,Get Equal Substrings Within Budget,Medium,https://leetcode.com/problems/get-equal-substrings-within-budget,Sliding Window, 10 | -------------------------------------------------------------------------------- /problem_tables/Sorting.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 274,H-Index,Medium, https://leetcode.com/problems/h-index," ""Sorting","[' Array""']" 3 | 527,Word Abbreviation,Hard,https://leetcode.com/problems/word-abbreviation,"String, Sorting, Greedy", 4 | 853,Car Fleet,Medium,https://leetcode.com/problems/car-fleet,"Sorting, Greedy", 5 | 630,Course Schedule III,Hard,https://leetcode.com/problems/course-schedule-iii,"Greedy, Sorting, Heap (Priority Queue)", 6 | 506,Relative Ranks,Easy,https://leetcode.com/problems/relative-ranks,"Array, Sorting", 7 | 522,Longest Uncommon Subsequence II,Medium,https://leetcode.com/problems/longest-uncommon-subsequence-ii,"String, Sorting", 8 | 954,Array of Doubled Pairs,Medium,https://leetcode.com/problems/array-of-doubled-pairs,"Array, Sorting", 9 | 1272,Remove Interval,Medium,https://leetcode.com/problems/remove-interval,"Array, Sorting", 10 | 1331,Rank Transform of an Array,Easy,https://leetcode.com/problems/rank-transform-of-an-array,"Array, Sorting", 11 | 1387,Sort Integers by The Power Value,Medium,https://leetcode.com/problems/sort-integers-by-the-power-value,"Dynamic Programming, Sorting", 12 | 1452,People Whose List of Favorite Companies Is Not a Subset of Another List,Medium,https://leetcode.com/problems/people-whose-list-of-favorite-companies-is-not-a-subset-of-another-list,"Hash Set, Sorting", 13 | 1471,The k Strongest Values in an Array,Medium,https://leetcode.com/problems/the-k-strongest-values-in-an-array,"Sorting, Two Pointers", 14 | 1508,Range Sum of Sorted Subarray Sums,Medium,https://leetcode.com/problems/range-sum-of-sorted-subarray-sums,"Array, Sorting, Binary Search", 15 | 1386,Cinema Seat Allocation,Medium,https://leetcode.com/problems/cinema-seat-allocation,"Greedy, Sorting", 16 | 571,Find Median Given Frequency of Numbers,Hard,https://leetcode.com/problems/find-median-given-frequency-of-numbers,"Sorting, Math", 17 | 1329,Sort the Matrix Diagonally,Medium,https://leetcode.com/problems/sort-the-matrix-diagonally,"Matrix, Sorting", 18 | 891,Sum of Subsequence Widths,Hard,https://leetcode.com/problems/sum-of-subsequence-widths,"Math, Sorting", 19 | 1353,Maximum Number of Events That Can Be Attended,Medium,https://leetcode.com/problems/maximum-number-of-events-that-can-be-attended,"Greedy, Sorting", 20 | 1030,Matrix Cells in Distance Order,Easy,https://leetcode.com/problems/matrix-cells-in-distance-order,"Breadth-First Search, Sorting", 21 | 1333,Filter Restaurants by Vegan-Friendly & Price and Distance,Medium,https://leetcode.com/problems/filter-restaurants-by-vegan-friendly-price-and-distance,"Sorting, Greedy", 22 | -------------------------------------------------------------------------------- /problem_tables/Topological_Sort.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1203,Sort Items by Groups Respecting Dependencies,Hard,https://leetcode.com/problems/sort-items-by-groups-respecting-dependencies,"Graph, Topological Sort", 3 | 802,Find Eventual Safe States,Medium,https://leetcode.com/problems/find-eventual-safe-states,"Graph, Depth-First Search, Breadth-First Search, Topological Sort", 4 | 1462,Course Schedule IV,Medium,https://leetcode.com/problems/course-schedule-iv,"Graph, Topological Sort", 5 | -------------------------------------------------------------------------------- /problem_tables/Trie.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 677,Map Sum Pairs,Medium, https://leetcode.com/problems/map-sum-pairs," ""Trie","[' Hash Table', ' Design""']" 3 | 472,Concatenated Words,Hard, https://leetcode.com/problems/concatenated-words," ""Trie","[' Depth-First Search', ' Dynamic Programming""']" 4 | 208,Implement Trie (Prefix Tree),Medium, https://leetcode.com/problems/implement-trie-prefix-tree," ""Trie","[' Hash Table', ' Design""']" 5 | 720,Longest Word in Dictionary,Easy, https://leetcode.com/problems/longest-word-in-dictionary," ""Trie","[' Array', ' Hash Table', ' String""']" 6 | 745,Prefix and Suffix Search,Hard, https://leetcode.com/problems/prefix-and-suffix-search," ""Trie","[' Design', ' String""']" 7 | 425,Word Squares,Hard,https://leetcode.com/problems/word-squares,"Backtracking, Trie", 8 | 648,Replace Words,Medium,https://leetcode.com/problems/replace-words,"Trie, Hash Map", 9 | -------------------------------------------------------------------------------- /problem_tables/Union_Find.csv: -------------------------------------------------------------------------------- 1 | ID,Title,Difficulty,Leetcode Question Link,Tags, 2 | 1319,Number of Operations to Make Network Connected,Medium, https://leetcode.com/problems/number-of-operations-to-make-network-connected," ""Union Find","[' Graph""']" 3 | 1202,Smallest String With Swaps,Medium, https://leetcode.com/problems/smallest-string-with-swaps," ""Union Find","[' Array""']" 4 | 1489,Find Critical and Pseudo-Critical Edges in Minimum Spanning Tree,Hard, https://leetcode.com/problems/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree," ""Union Find","[' Graph""']" 5 | 803,Bricks Falling When Hit,Hard, https://leetcode.com/problems/bricks-falling-when-hit," ""Union Find","[' Graph""']" 6 | 947,Most Stones Removed with Same Row or Column,Medium,https://leetcode.com/problems/most-stones-removed-with-same-row-or-column,"Union Find, Graph, Depth-First Search", 7 | 685,Redundant Connection II,Hard,https://leetcode.com/problems/redundant-connection-ii,"Union Find, Graph", 8 | 952,Largest Component Size by Common Factor,Hard,https://leetcode.com/problems/largest-component-size-by-common-factor,"Union Find, Graph", 9 | 1391,Check if There is a Valid Path in a Grid,Medium,https://leetcode.com/problems/check-if-there-is-a-valid-path-in-a-grid,"Graph, Union Find", 10 | 990,Satisfiability of Equality Equations,Medium,https://leetcode.com/problems/satisfiability-of-equality-equations,"Graph, Union Find", 11 | 959,Regions Cut By Slashes,Medium,https://leetcode.com/problems/regions-cut-by-slashes,"Union Find, Graph", 12 | -------------------------------------------------------------------------------- /src/array/binary_search/find-closest-elements.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | function findClosestElements(arr: number[], k: number, x: number): number[] { 4 | let left = 0; 5 | let right = arr.length - k; 6 | 7 | while (left < right) { 8 | const mid = Math.floor((left + right) / 2); 9 | if (x - arr[mid] > arr[mid + k] - x) { 10 | left = mid + 1; 11 | } else { 12 | right = mid; 13 | } 14 | } 15 | 16 | return arr.slice(left, left + k); 17 | } 18 | 19 | export function findClosestElementsDBG(){ 20 | const tests = [ 21 | { 22 | input: { arr: [1, 2, 3, 4, 5, 6, 7, 8, 9], k: 4, x: 3 }, 23 | result: [1, 2, 3, 4] 24 | }, 25 | { 26 | input: { arr: [1, 2, 3, 4, 5], k: 4, x: -1 }, 27 | result: [1, 2, 3, 4] 28 | } 29 | ]; 30 | 31 | tests.forEach((test, index) => { 32 | const result = findClosestElements(test.input.arr, test.input.k, test.input.x); 33 | const success = JSON.stringify(result) === JSON.stringify(test.result); 34 | if (success) { 35 | console.log(`${index} success`); 36 | } else { 37 | console.log(`${index} fail`); 38 | console.log(`expected ${test.result}`); 39 | console.log(`got ${result}`); 40 | } 41 | }); 42 | } -------------------------------------------------------------------------------- /src/array/binary_search/longest-Increasing-subseq.ts: -------------------------------------------------------------------------------- 1 | function lengthOfLIS(nums: number[]): number { 2 | const sub: number[] = []; 3 | 4 | for (let num of nums) { 5 | let left = 0, right = sub.length; 6 | 7 | while (left < right) { 8 | const mid = Math.floor((left + right) / 2); 9 | if (sub[mid] < num) { 10 | left = mid + 1; 11 | } else { 12 | right = mid; 13 | } 14 | } 15 | 16 | if (left < sub.length) { 17 | sub[left] = num; 18 | } else { 19 | sub.push(num); 20 | } 21 | } 22 | 23 | return sub.length; 24 | } 25 | 26 | 27 | export function lengthOfLISDBG(){ 28 | const tests = [ 29 | { 30 | input: [10, 9, 2, 5, 3, 7, 101, 18], 31 | expected: 4 // Длина LIS: [2, 3, 7, 101] 32 | }, 33 | { 34 | input: [0, 1, 0, 3, 2, 3], 35 | expected: 4 // Длина LIS: [0, 1, 2, 3] 36 | }, 37 | { 38 | input: [7, 7, 7, 7, 7, 7, 7], 39 | expected: 1 // Все числа одинаковы, длина LIS: 1 40 | } 41 | ]; 42 | 43 | tests.forEach((test, index) => { 44 | const result = lengthOfLIS(test.input); 45 | const success = result === test.expected; 46 | if (success) { 47 | console.log(`Test ${index} success`); 48 | } else { 49 | console.log(`Test ${index} fail`); 50 | console.log(`expected: ${test.expected}`); 51 | console.log(`got: ${result}`); 52 | } 53 | }); 54 | } -------------------------------------------------------------------------------- /src/array/binary_search/search-in-rotated-sorted-array.ts: -------------------------------------------------------------------------------- 1 | function search(nums: number[], target: number): number { 2 | let left = 0, right = nums.length-1; 3 | while(left <= right){ 4 | const mid = Math.floor((left+right)/2); 5 | const num = nums[mid]; 6 | if(target == num){ 7 | return mid; 8 | } 9 | if(nums[left] <= num){ 10 | if(target >= nums[left] && target <= num){ 11 | right = mid-1; 12 | } else{ 13 | left = mid+1; 14 | } 15 | } else{ 16 | if(nums[right] >= target && target >= num){ 17 | left = mid+1; 18 | } else{ 19 | right = mid-1; 20 | } 21 | } 22 | } 23 | return -1; 24 | }; 25 | 26 | export function searchDBG() { 27 | const tests = [ 28 | { 29 | input: { nums: [4,5,6,7,0,1,2], target: 0 }, 30 | result: 4 31 | }, 32 | { 33 | input: { nums: [4,5,6,7,0,1,2], target: 3 }, 34 | result: -1 35 | }, 36 | { 37 | input: { nums: [1], target: 0 }, 38 | result: -1 39 | }, 40 | { 41 | input: { nums: [1], target: 1 }, 42 | result: 0 43 | } 44 | ]; 45 | 46 | tests.forEach((test, index) => { 47 | const result = search(test.input.nums, test.input.target); 48 | if (result === test.result) { 49 | console.log(`${index} success`); 50 | } else { 51 | console.log(`${index} fail`); 52 | console.log(`expected ${test.result}`); 53 | console.log(`got ${result}`); 54 | } 55 | }); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/array/bs/koko-eating-bananas.ts: -------------------------------------------------------------------------------- 1 | function minEatingSpeed(piles: number[], h: number): number { 2 | const timeToEat = (k: number) => { 3 | let hours = 0; 4 | for (const pile of piles) { 5 | hours += Math.ceil(pile / k); 6 | } 7 | return hours 8 | } 9 | let l = 1, r = Math.max(...piles) - 1; 10 | while (l <= r) { 11 | const mid = Math.floor((l + r) / 2); 12 | const time = timeToEat(mid); 13 | if (time <= h) { 14 | r = mid - 1; 15 | } else { 16 | l = mid + 1; 17 | } 18 | } 19 | return l; 20 | }; 21 | 22 | 23 | export function minEatingSpeedDBG() { 24 | const tests = [ 25 | { 26 | input: { piles: [3, 6, 7, 11], h: 8 }, 27 | expected: 4 // Минимальная скорость 4 банана в час 28 | }, 29 | { 30 | input: { piles: [30, 11, 23, 4, 20], h: 5 }, 31 | expected: 30 // Минимальная скорость 30 бананов в час 32 | }, 33 | { 34 | input: { piles: [30, 11, 23, 4, 20], h: 6 }, 35 | expected: 23 // Минимальная скорость 23 банана в час 36 | } 37 | ]; 38 | 39 | tests.forEach((testCase, index) => { 40 | const result = minEatingSpeed(testCase.input.piles, testCase.input.h); 41 | const success = result === testCase.expected; 42 | if (success) { 43 | console.log(`Test ${index} success`); 44 | } else { 45 | console.log(`Test ${index} fail`); 46 | console.log(`expected: ${testCase.expected}`); 47 | console.log(`got: ${result}`); 48 | } 49 | }); 50 | 51 | } -------------------------------------------------------------------------------- /src/array/bs/search-2D-Matrix-II.ts: -------------------------------------------------------------------------------- 1 | function searchMatrix(matrix: number[][], target: number): boolean { 2 | for (let i of matrix) { 3 | let l = 0, r = i.length - 1, mid = 0; 4 | while (l <= r) { 5 | mid = Math.floor((l + r) / 2) 6 | if (i[mid] === target) 7 | return true; 8 | else if (i[mid] > target) 9 | r = mid - 1; 10 | else 11 | l = mid + 1 12 | } 13 | } 14 | return false 15 | }; -------------------------------------------------------------------------------- /src/array/count-primes.ts: -------------------------------------------------------------------------------- 1 | function countPrimes(n: number): number { 2 | if (n <= 2) return 0; 3 | 4 | const isPrime: boolean[] = new Array(n).fill(true); 5 | isPrime[0] = false; 6 | isPrime[1] = false; 7 | 8 | for (let i = 2; i * i < n; i++) { 9 | if (isPrime[i]) { 10 | for (let j = i * i; j < n; j += i) { 11 | isPrime[j] = false; 12 | } 13 | } 14 | } 15 | 16 | let count = 0; 17 | for (let i = 2; i < n; i++) { 18 | if (isPrime[i]) count++; 19 | } 20 | return count; 21 | } 22 | -------------------------------------------------------------------------------- /src/array/dp/num-squares.ts: -------------------------------------------------------------------------------- 1 | function numSquares(n: number): number { 2 | // Инициализируем массив dp, где dp[i] будет хранить наименьшее количество квадратов, сумма которых равна i 3 | const dp: number[] = new Array(n + 1).fill(Infinity); 4 | dp[0] = 0; // База: сумма 0 требует 0 чисел 5 | 6 | // Предварительно вычисляем все возможные квадратные числа меньше или равные n 7 | const squares: number[] = []; 8 | for (let i = 1; i * i <= n; i++) { 9 | squares.push(i * i); 10 | } 11 | 12 | // Заполняем массив dp 13 | for (let i = 1; i <= n; i++) { 14 | for (const square of squares) { 15 | if (square > i) break; 16 | dp[i] = Math.min(dp[i], dp[i - square] + 1); 17 | } 18 | } 19 | 20 | return dp[n]; 21 | } -------------------------------------------------------------------------------- /src/array/hash_table/intersection.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | function intersection(nums1: number[], nums2: number[]): number[] { 4 | const set1 = new Set(nums1); 5 | const set2 = new Set(nums2); 6 | 7 | const result: number[] = []; 8 | set1.forEach(value => { 9 | if (set2.has(value)) { 10 | result.push(value); 11 | } 12 | }); 13 | 14 | return result; 15 | } 16 | 17 | 18 | export function intersectionDBG() { 19 | const tests = [ 20 | { 21 | input: { nums1: [1, 2, 2, 1], nums2: [2, 2] }, 22 | result: [2] 23 | }, 24 | { 25 | input: { nums1: [4, 9, 5], nums2: [9, 4, 9, 8, 4] }, 26 | result: [4, 9] 27 | } 28 | ]; 29 | 30 | tests.forEach((test, index) => { 31 | const result = intersection(test.input.nums1, test.input.nums2); 32 | const success = JSON.stringify(result.sort()) === JSON.stringify(test.result.sort()); 33 | if (success) { 34 | console.log(`${index} success`); 35 | } else { 36 | console.log(`${index} fail`); 37 | console.log(`expected ${JSON.stringify(test.result)}`); 38 | console.log(`got ${JSON.stringify(result)}`); 39 | } 40 | }); 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/array/hash_table/intersectionII.ts: -------------------------------------------------------------------------------- 1 | function intersectII(nums1: number[], nums2: number[]): number[] { 2 | const map = new Map(); 3 | const result: number[] = []; 4 | 5 | for (const num of nums1) { 6 | map.set(num, (map.get(num) || 0) + 1); 7 | } 8 | 9 | for (const num of nums2) { 10 | if (map.has(num) && map.get(num)! > 0) { 11 | result.push(num); 12 | map.set(num, map.get(num)! - 1); 13 | } 14 | } 15 | return result; 16 | } 17 | 18 | function intersectIIDBG(){ 19 | const tests = [ 20 | { 21 | input: { nums1: [1, 2, 2, 1], nums2: [2, 2] }, 22 | result: [2, 2] 23 | }, 24 | { 25 | input: { nums1: [4, 9, 5], nums2: [9, 4, 9, 8, 4] }, 26 | result: [4, 9] 27 | } 28 | ]; 29 | 30 | tests.forEach((test, index) => { 31 | const result = intersectII(test.input.nums1, test.input.nums2); 32 | const success = JSON.stringify(result.sort()) === JSON.stringify(test.result.sort()); 33 | if (success) { 34 | console.log(`${index} success`); 35 | } else { 36 | console.log(`${index} fail`); 37 | console.log(`expected ${JSON.stringify(test.result)}`); 38 | console.log(`got ${JSON.stringify(result)}`); 39 | } 40 | }); 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/array/hash_table/is-reflected.ts: -------------------------------------------------------------------------------- 1 | function isReflected(points: number[][]): boolean { 2 | const pointSet = new Set(); 3 | let minX = Infinity; 4 | let maxX = -Infinity; 5 | 6 | for (const [x, y] of points) { 7 | minX = Math.min(minX, x); 8 | maxX = Math.max(maxX, x); 9 | pointSet.add(`${x},${y}`); 10 | } 11 | 12 | const midX = (minX + maxX) / 2; 13 | const cmpr = midX * 2; 14 | 15 | for (const [x, y] of points) { 16 | const reflectedX = cmpr - x; 17 | if (!pointSet.has(`${reflectedX},${y}`)) { 18 | return false; 19 | } 20 | } 21 | 22 | return true; 23 | } 24 | 25 | export function isReflectedDBG() { 26 | const tests = [ 27 | { 28 | // Ожидаемый результат: false 29 | // Нет симметрии, так как точка (-2, 1) не имеет "зеркальной" пары 30 | input: [[1, 1], [-2, 1], [1, -1], [-1, -1]], 31 | result: false 32 | }, 33 | { 34 | // Ожидаемый результат: true 35 | // Точки симметричны относительно оси X = 0 36 | input: [[1, 1], [-1, 1], [1, -1], [-1, -1]], 37 | result: true 38 | }, 39 | { 40 | input: [[1, 1], [-1, 1]], 41 | result: true 42 | }, 43 | { 44 | input: [[1, 1], [-1, -1]], 45 | result: false 46 | }, 47 | { 48 | // Ожидаемый результат: true 49 | // Точки симметричны относительно оси X = 0, включая точку на самой оси 50 | input: [[1, 2], [-1, 2], [1, 3], [-1, 3], [0, 4]], 51 | result: true 52 | }, 53 | 54 | ]; 55 | 56 | tests.forEach((test, index) => { 57 | const result = isReflected(test.input); 58 | const success = result === test.result; 59 | if (success) { 60 | console.log(`${index} success`); 61 | } else { 62 | console.log(`${index} fail`); 63 | console.log(`expected ${test.result}`); 64 | console.log(`got ${result}`); 65 | } 66 | }); 67 | } -------------------------------------------------------------------------------- /src/array/hash_table/two-sum.ts: -------------------------------------------------------------------------------- 1 | function twoSumSorted(nums: number[], target: number): number[] | null { 2 | let left = 0; 3 | let right = nums.length - 1; 4 | 5 | while (left < right) { 6 | const sum = nums[left] + nums[right]; 7 | 8 | if (sum === target) { 9 | return [left, right]; 10 | } else if (sum < target) { 11 | left++; 12 | } else { 13 | right--; 14 | } 15 | } 16 | return null; 17 | } 18 | -------------------------------------------------------------------------------- /src/array/intervals/erase-overlap-intervals.ts: -------------------------------------------------------------------------------- 1 | function eraseOverlapIntervals(intervals: number[][]): number { 2 | if (intervals.length === 0) return 0; 3 | 4 | intervals.sort((a, b) => a[1] - b[1]); 5 | 6 | let count = 0; 7 | let prevEnd = intervals[0][1]; 8 | 9 | for (let i = 1; i < intervals.length; i++) { 10 | if (intervals[i][0] < prevEnd) { 11 | count++; 12 | } else { 13 | prevEnd = intervals[i][1]; 14 | } 15 | } 16 | return count; 17 | } 18 | 19 | export function eraseOverlapIntervalsDBG() { 20 | const tests = [ 21 | { 22 | input: [[1, 2], [2, 3], [3, 4], [1, 3]], 23 | expected: 1 // Удаляем [1, 3], чтобы интервалы не пересекались 24 | }, 25 | { 26 | input: [[1, 2], [1, 2], [1, 2]], 27 | expected: 2 // Нужно удалить 2 интервала 28 | }, 29 | { 30 | input: [[1, 2], [2, 3]], 31 | expected: 0 // Интервалы не пересекаются 32 | } 33 | ]; 34 | 35 | tests.forEach((testCase, index) => { 36 | const result = eraseOverlapIntervals(testCase.input); 37 | const success = result === testCase.expected; 38 | if (success) { 39 | console.log(`Test ${index} success`); 40 | } else { 41 | console.log(`Test ${index} fail`); 42 | console.log(`expected: ${testCase.expected}`); 43 | console.log(`got: ${result}`); 44 | } 45 | }); 46 | 47 | } -------------------------------------------------------------------------------- /src/array/intervals/insert-interval.ts: -------------------------------------------------------------------------------- 1 | function insert(intervals: number[][], newInterval: number[]): number[][] { 2 | const result: number[][] = []; 3 | let i = 0; 4 | const n = intervals.length; 5 | 6 | while (i < n && intervals[i][1] < newInterval[0]) { 7 | result.push(intervals[i]); 8 | i++; 9 | } 10 | while (i < n && intervals[i][0] <= newInterval[1]) { 11 | newInterval[0] = Math.min(newInterval[0], intervals[i][0]); 12 | newInterval[1] = Math.max(newInterval[1], intervals[i][1]); 13 | i++; 14 | } 15 | result.push(newInterval); 16 | 17 | while (i < n) { 18 | result.push(intervals[i]); 19 | i++; 20 | } 21 | return result; 22 | }; 23 | 24 | 25 | export function insertDBG() { 26 | const tests = [ 27 | { 28 | input: { intervals: [[1, 3], [6, 9]], newInterval: [2, 5] }, 29 | expected: [[1, 5], [6, 9]] // Новый интервал перекрывает первый интервал, нужно объединить их. 30 | }, 31 | { 32 | input: { intervals: [[1, 2], [3, 5], [6, 7], [8, 10], [12, 16]], newInterval: [4, 8] }, 33 | expected: [[1, 2], [3, 10], [12, 16]] // Новый интервал перекрывает три существующих интервала, нужно объединить. 34 | }, 35 | { 36 | input: { intervals: [], newInterval: [5, 7] }, 37 | expected: [[5, 7]] // Пустой список интервалов, добавляем новый интервал. 38 | }, 39 | { 40 | input: { intervals: [[1, 5]], newInterval: [2, 3] }, 41 | expected: [[1, 5]] // Новый интервал полностью содержится в первом интервале, ничего не изменяется. 42 | } 43 | ]; 44 | 45 | tests.forEach((testCase, index) => { 46 | const result = insert(testCase.input.intervals, testCase.input.newInterval); 47 | const success = JSON.stringify(result) === JSON.stringify(testCase.expected); 48 | if (success) { 49 | console.log(`Test ${index} success`); 50 | } else { 51 | console.log(`Test ${index} fail`); 52 | console.log(`expected: ${testCase.expected}`); 53 | console.log(`got: ${result}`); 54 | } 55 | }); 56 | } -------------------------------------------------------------------------------- /src/array/intervals/interval-intersection.ts: -------------------------------------------------------------------------------- 1 | function intervalIntersection(A: number[][], B: number[][]): number[][] { 2 | const result: number[][] = []; 3 | let i = 0; 4 | let j = 0; 5 | 6 | while (i < A.length && j < B.length) { 7 | const startMax = Math.max(A[i][0], B[j][0]); 8 | const endMin = Math.min(A[i][1], B[j][1]); 9 | 10 | // Если интервалы пересекаются 11 | if (startMax <= endMin) { 12 | result.push([startMax, endMin]); 13 | } 14 | 15 | // Двигаем указатель того интервала, который заканчивается раньше 16 | if (A[i][1] < B[j][1]) { 17 | i++; 18 | } else { 19 | j++; 20 | } 21 | } 22 | return result; 23 | } 24 | -------------------------------------------------------------------------------- /src/array/intervals/merge-intervals.ts: -------------------------------------------------------------------------------- 1 | function mergeIntervals(intervals: [number, number][]): [number, number][] { 2 | if (intervals.length === 0) return []; 3 | intervals.sort((a, b) => a[0] - b[0]); 4 | const result: [number, number][] = []; 5 | let currentInterval = intervals[0]; 6 | 7 | for (let i = 1; i < intervals.length; i++) { 8 | if (currentInterval[1] >= intervals[i][0]) { 9 | currentInterval[1] = Math.max(currentInterval[1], intervals[i][1]); 10 | } else { 11 | result.push(currentInterval); 12 | currentInterval = intervals[i]; 13 | } 14 | } 15 | result.push(currentInterval); 16 | return result; 17 | } 18 | 19 | export function mergeDBG() { 20 | const tests = [ 21 | { 22 | input: [[1, 3], [2, 6], [8, 10], [15, 18]], 23 | result: [[1, 6], [8, 10], [15, 18]] 24 | }, 25 | { 26 | input: [[1, 4], [4, 5]], 27 | result: [[1, 5]] 28 | }, 29 | { 30 | input: [[1, 4], [2, 3]], 31 | result: [[1, 4]] 32 | } 33 | ]; 34 | 35 | tests.forEach((test, index) => { 36 | const intr: [number, number][] = [[1,2]]; 37 | 38 | const result = mergeIntervals(test.input as [number, number][]); 39 | 40 | const success = JSON.stringify(result.sort()) === JSON.stringify(test.result.sort()); 41 | if (success) { 42 | console.log(`${index} success`); 43 | } else { 44 | console.log(`${index} fail`); 45 | console.log(`expected ${JSON.stringify(test.result)}`); 46 | console.log(`got ${JSON.stringify(result)}`); 47 | } 48 | }); 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/array/intervals/min-meeting-rooms.ts: -------------------------------------------------------------------------------- 1 | function minMeetingRooms(intervals: number[][]): number { 2 | if (intervals.length === 0) return 0; 3 | 4 | const startTimes = intervals.map(i => i[0]).sort((a, b) => a - b); 5 | const endTimes = intervals.map(i => i[1]).sort((a, b) => a - b); 6 | 7 | let startPointer = 0; 8 | let endPointer = 0; 9 | let roomsNeeded = 0; 10 | let maxRooms = 0; 11 | 12 | while (startPointer < intervals.length) { 13 | if (startTimes[startPointer] < endTimes[endPointer]) { 14 | roomsNeeded++; 15 | startPointer++; 16 | } else { 17 | roomsNeeded--; 18 | endPointer++; 19 | } 20 | maxRooms = Math.max(maxRooms, roomsNeeded); 21 | } 22 | 23 | return maxRooms; 24 | } 25 | 26 | export function minMeetingRoomsDBG(){ 27 | const tests = [ 28 | { 29 | input: [[0, 30], [5, 10], [15, 20]], 30 | result: 2 31 | }, 32 | { 33 | input: [[7, 10], [2, 4]], 34 | result: 1 35 | } 36 | ]; 37 | 38 | tests.forEach((test, index) => { 39 | const result = minMeetingRooms(test.input); 40 | if (result === test.result) { 41 | console.log(`${index} success`); 42 | } else { 43 | console.log(`${index} fail`); 44 | console.log(`expected ${test.result}`); 45 | console.log(`got ${result}`); 46 | } 47 | }); 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/array/intervals/remove-covered-intervals.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @problem [1288. Remove Covered Intervals](https://leetcode.com/problems/remove-covered-intervals) 3 | * 4 | * ### Задача тупорылая на самом LeetCode 5 | * Схуяли он считает, что если прилетает 1 интервал, то ответ 1? Задача проверяется тупорыло 6 | * Если прилетает на собесе, то обязательно надо уточнять условия, иначае такой алгос в реале 7 | * не будет работать 8 | * 9 | * ### Чтобы решить задаччу 10 | * В этом решении нет необходимости обращать внимание на начало интервала после сортировки, 11 | * потому что мы уже гарантировали правильный порядок интервалов. Теперь можно просто 12 | * отслеживать максимальный конец и игнорировать покрытые интервалы. 13 | */ 14 | function removeCoveredIntervals(intervals: number[][]): number { 15 | intervals.sort((a, b) => 16 | a[0] === b[0] ? b[1] - a[1] : a[0] - b[0] 17 | ); 18 | let count = 0; 19 | let maxEnd = 0; 20 | 21 | for (const [start, end] of intervals) { 22 | if (end > maxEnd) { 23 | count++; 24 | maxEnd = end; 25 | } 26 | } 27 | return count; 28 | }; -------------------------------------------------------------------------------- /src/array/intervals/remove-interval.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @problem [1288. Remove Covered Intervals](https://leetcode.com/problems/remove-covered-intervals) 3 | * 4 | * ### Как решается 5 | * Задачу можно решить, обрабатывая каждый интервал и проверяя его пересечение 6 | * с интервалом toBeRemoved. Для каждого интервала возможны три случая: 7 | * 1. Интервал полностью лежит вне toBeRemoved: В этом случае интервал остается неизменным. 8 | * 2. Интервал полностью лежит внутри toBeRemoved: В этом случае интервал удаляется. 9 | * 3. Интервал частично пересекается с toBeRemoved: В этом случае мы разделяем интервал на 10 | * две части — до и после пересечения с toBeRemoved. 11 | */ 12 | function removeInterval(intervals: number[][], toBeRemoved: number[]): number[][] { 13 | const result: number[][] = []; 14 | const [removeStart, removeEnd] = toBeRemoved; 15 | 16 | for (const [start, end] of intervals) { 17 | if (end <= removeStart || start >= removeEnd) { 18 | result.push([start, end]); 19 | } else { 20 | if (start < removeStart) { 21 | result.push([start, removeStart]); 22 | } 23 | if (end > removeEnd) { 24 | result.push([removeEnd, end]); 25 | } 26 | } 27 | } 28 | 29 | return result; 30 | } 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | function removeIntervalDBG(){ 46 | const tests = [ 47 | { 48 | input: { 49 | intervals: [[0, 2], [3, 4], [5, 7]], 50 | toBeRemoved: [1, 6] 51 | }, 52 | expected: [[0, 1], [6, 7]] 53 | }, 54 | { 55 | input: { 56 | intervals: [[0, 5]], 57 | toBeRemoved: [2, 3] 58 | }, 59 | expected: [[0, 2], [3, 5]] 60 | }, 61 | { 62 | input: { 63 | intervals: [[0, 100]], 64 | toBeRemoved: [50, 60] 65 | }, 66 | expected: [[0, 50], [60, 100]] 67 | } 68 | ]; 69 | 70 | tests.forEach((testCase, index) => { 71 | const result = removeInterval(testCase.input.intervals, testCase.input.toBeRemoved); 72 | const success = JSON.stringify(result) === JSON.stringify(testCase.expected); 73 | if (success) { 74 | console.log(`Test ${index} success`); 75 | } else { 76 | console.log(`Test ${index} fail`); 77 | console.log(`expected: ${JSON.stringify(testCase.expected)}`); 78 | console.log(`got: ${JSON.stringify(result)}`); 79 | } 80 | }); 81 | 82 | } -------------------------------------------------------------------------------- /src/array/intervals/summary-ranges.ts: -------------------------------------------------------------------------------- 1 | function summaryRanges(nums: number[]): string[] { 2 | if (nums.length === 0) return []; 3 | const result: string[] = []; 4 | let start = nums[0]; 5 | 6 | for (let i = 1; i <= nums.length; i++) { 7 | if (i === nums.length || nums[i] !== nums[i - 1] + 1) { 8 | if (start === nums[i - 1]) { 9 | result.push(`${start}`); 10 | } else { 11 | result.push(`${start}->${nums[i - 1]}`); 12 | } 13 | if (i < nums.length) { 14 | start = nums[i]; 15 | } 16 | } 17 | } 18 | return result; 19 | } 20 | 21 | function summaryRangesDBG(){ 22 | const tests = [ 23 | { 24 | input: [0, 1, 2, 4, 5, 7], 25 | result: ["0->2", "4->5", "7"] 26 | }, 27 | { 28 | input: [0, 2, 3, 4, 6, 8, 9], 29 | result: ["0", "2->4", "6", "8->9"] 30 | }, 31 | { 32 | input: [], 33 | result: [] 34 | }, 35 | { 36 | input: [-1], 37 | result: ["-1"] 38 | }, 39 | { 40 | input: [0], 41 | result: ["0"] 42 | } 43 | ]; 44 | 45 | tests.forEach((test, index) => { 46 | const result = summaryRanges(test.input); 47 | const success = JSON.stringify(result) === JSON.stringify(test.result); 48 | if (success) { 49 | console.log(`${index} success`); 50 | } else { 51 | console.log(`${index} fail`); 52 | console.log(`expected ${JSON.stringify(test.result)}`); 53 | console.log(`got ${JSON.stringify(result)}`); 54 | } 55 | }); 56 | } -------------------------------------------------------------------------------- /src/array/is-palindrome.ts: -------------------------------------------------------------------------------- 1 | function isPalindrome(x: number): boolean { 2 | if (x < 0 || (x % 10 === 0 && x !== 0)) { 3 | return false; 4 | } 5 | 6 | let reversed = 0; 7 | let original = x; 8 | 9 | while (original > reversed) { 10 | reversed = reversed * 10 + original % 10; 11 | original = Math.floor(original / 10); 12 | } 13 | 14 | return original === reversed || original === Math.floor(reversed / 10); 15 | } 16 | -------------------------------------------------------------------------------- /src/array/majority-element.ts: -------------------------------------------------------------------------------- 1 | function majorityElement(nums: number[]): number { 2 | let candidate = null; 3 | let count = 0; 4 | 5 | for (let num of nums) { 6 | if (count === 0) { 7 | candidate = num; 8 | } 9 | count += (num === candidate) ? 1 : -1; 10 | } 11 | return candidate!; 12 | } 13 | 14 | export function majorityElementDBG() { 15 | const tests = [ 16 | { 17 | input: [3, 2, 3], 18 | result: 3 19 | }, 20 | { 21 | input: [1, 2, 2, 1, 1, 2, 2], 22 | result: 2 23 | } 24 | ]; 25 | 26 | tests.forEach((test, index) => { 27 | const result = majorityElement(test.input); 28 | if (result === test.result) { 29 | console.log(`${index} success`); 30 | } else { 31 | console.log(`${index} fail`); 32 | console.log(`expected ${test.result}`); 33 | console.log(`got ${result}`); 34 | } 35 | }); 36 | } -------------------------------------------------------------------------------- /src/array/matrix/equal-pairs.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @problem [2352. Equal Row and Column Pairs](https://leetcode.com/problems/equal-row-and-column-pairs) 3 | * 4 | * ### Решаем с помощью хеширования 5 | * 6 | */ 7 | function equalPairs(grid: number[][]): number { 8 | const n = grid.length; 9 | const hash = new Map(); 10 | let pairs = 0; 11 | 12 | for (let i = 0; i < n; i++) { 13 | const row = grid[i].join(','); 14 | hash.set(row, (hash.get(row) || 0) + 1); 15 | } 16 | for (let j = 0; j < n; j++) { 17 | const column = []; 18 | for (let i = 0; i < n; i++) { 19 | column.push(grid[i][j]); 20 | } 21 | const colKey = column.join(','); 22 | if (hash.has(colKey)) { 23 | pairs += hash.get(colKey); 24 | } 25 | } 26 | return pairs; 27 | }; 28 | 29 | 30 | export function checkEqualPairsDBG() { 31 | const tests = [ 32 | { 33 | input: { 34 | grid: [ 35 | [3, 2, 1], 36 | [1, 7, 6], 37 | [2, 7, 7] 38 | ] 39 | }, 40 | result: 1 // Столбец [3,1,2] совпадает со строкой [3,2,1] => 1 пара 41 | }, 42 | { 43 | input: { 44 | grid: [ 45 | [3, 1, 2, 2], 46 | [1, 4, 4, 5], 47 | [2, 4, 2, 2], 48 | [2, 4, 2, 2] 49 | ] 50 | }, 51 | result: 3 // Строки [2,4,2,2] дважды совпадают со столбцом [2,4,2,2] 52 | }, 53 | { 54 | input: { 55 | grid: [ 56 | [1, 2], 57 | [2, 1] 58 | ] 59 | }, 60 | result: 0 // Нет совпадений строк и столбцов 61 | }, 62 | { 63 | input: { 64 | grid: [ 65 | [1, 1], 66 | [1, 1] 67 | ] 68 | }, 69 | result: 4 // Все строки равны столбцам 70 | } 71 | ]; 72 | 73 | tests.forEach((test, index) => { 74 | const result = equalPairs(test.input.grid); 75 | const success = result === test.result; 76 | if (success) { 77 | console.log(`${index} success`); 78 | } else { 79 | console.log(`${index} fail`); 80 | console.log(`expected ${test.result}`); 81 | console.log(`got ${result}`); 82 | } 83 | }); 84 | } 85 | -------------------------------------------------------------------------------- /src/array/matrix/generate-matrix.ts: -------------------------------------------------------------------------------- 1 | function generateMatrix(n: number): number[][] { 2 | const matrix: number[][] = new Array(n) 3 | .map(() => Array(n).fill(0)); 4 | 5 | let num = 1; 6 | let top = 0; 7 | let bottom = n - 1; 8 | let left = 0; 9 | let right = n - 1; 10 | 11 | while (top <= bottom && left <= right) { 12 | // Заполнение верхней строки слева направо 13 | for (let j = left; j <= right; j++) { 14 | matrix[top][j] = num++; 15 | } 16 | top++; 17 | // Заполнение правого столбца сверху вниз 18 | for (let i = top; i <= bottom; i++) { 19 | matrix[i][right] = num++; 20 | } 21 | right--; 22 | if (top <= bottom) { 23 | // Заполнение нижней строки справа налево 24 | for (let j = right; j >= left; j--) { 25 | matrix[bottom][j] = num++; 26 | } 27 | bottom--; 28 | } 29 | if (left <= right) { 30 | // Заполнение левого столбца снизу вверх 31 | for (let i = bottom; i >= top; i--) { 32 | matrix[i][left] = num++; 33 | } 34 | left++; 35 | } 36 | } 37 | 38 | return matrix; 39 | } 40 | 41 | export function generateMatrixDBG() { 42 | const tests = [ 43 | { 44 | input: 3, 45 | result: [ 46 | [1, 2, 3], 47 | [8, 9, 4], 48 | [7, 6, 5] 49 | ] 50 | }, 51 | { 52 | input: 1, 53 | result: [ 54 | [1] 55 | ] 56 | }, 57 | { 58 | input: 4, 59 | result: [ 60 | [1, 2, 3, 4], 61 | [12, 13, 14, 5], 62 | [11, 16, 15, 6], 63 | [10, 9, 8, 7] 64 | ] 65 | } 66 | ]; 67 | 68 | tests.forEach((test, index) => { 69 | const result = generateMatrix(test.input); 70 | const success = JSON.stringify(result) === JSON.stringify(test.result); 71 | if (success) { 72 | console.log(`${index} success`); 73 | } else { 74 | console.log(`${index} fail`); 75 | console.log(`expected ${JSON.stringify(test.result)}`); 76 | console.log(`got ${JSON.stringify(result)}`); 77 | } 78 | }); 79 | } -------------------------------------------------------------------------------- /src/array/matrix/rotate-matrix.ts: -------------------------------------------------------------------------------- 1 | function rotate(matrix: number[][]): void { 2 | const n = matrix.length; 3 | for (let i = 0; i < n; i++) { 4 | for (let j = i + 1; j < n; j++) { 5 | [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]]; 6 | } 7 | } 8 | for (let i = 0; i < n; i++) { 9 | matrix[i].reverse(); 10 | } 11 | } 12 | 13 | 14 | function testRotate() { 15 | const tests = [ 16 | { 17 | input: [ 18 | [1, 2, 3], 19 | [4, 5, 6], 20 | [7, 8, 9] 21 | ], 22 | expected: [ 23 | [7, 4, 1], 24 | [8, 5, 2], 25 | [9, 6, 3] 26 | ] 27 | }, 28 | { 29 | input: [ 30 | [5, 1, 9, 11], 31 | [2, 4, 8, 10], 32 | [13, 3, 6, 7], 33 | [15, 14, 12, 16] 34 | ], 35 | expected: [ 36 | [15, 13, 2, 5], 37 | [14, 3, 4, 1], 38 | [12, 6, 8, 9], 39 | [16, 7, 10, 11] 40 | ] 41 | }, 42 | { 43 | input: [ 44 | [1] 45 | ], 46 | expected: [ 47 | [1] 48 | ] 49 | } 50 | ]; 51 | 52 | // Функция для сравнения двух матриц 53 | function matricesAreEqual(mat1: number[][], mat2: number[][]): boolean { 54 | if (mat1.length !== mat2.length) return false; 55 | for (let i = 0; i < mat1.length; i++) { 56 | if (mat1[i].length !== mat2[i].length) return false; 57 | for (let j = 0; j < mat1[i].length; j++) { 58 | if (mat1[i][j] !== mat2[i][j]) return false; 59 | } 60 | } 61 | return true; 62 | } 63 | 64 | tests.forEach((test, index) => { 65 | rotate(test.input); 66 | const success = matricesAreEqual(test.input, test.expected); 67 | if (success) { 68 | console.log(`Test ${index} success`); 69 | } else { 70 | console.log(`Test ${index} fail`); 71 | console.log(`expected: ${JSON.stringify(test.expected)}`); 72 | console.log(`got: ${JSON.stringify(test.input)}`); 73 | } 74 | }); 75 | } -------------------------------------------------------------------------------- /src/array/max-consecutive-ones.ts: -------------------------------------------------------------------------------- 1 | function findMaxConsecutiveOnes(nums: number[]): number { 2 | let maxCount = 0; 3 | let currentCount = 0; 4 | 5 | for (const num of nums) { 6 | if (num === 1) { 7 | currentCount++; 8 | maxCount = Math.max(maxCount, currentCount); 9 | } else { 10 | currentCount = 0; 11 | } 12 | } 13 | 14 | return maxCount; 15 | } 16 | -------------------------------------------------------------------------------- /src/array/max-profit.ts: -------------------------------------------------------------------------------- 1 | export function maxProfit(prices: number[]): number { 2 | let minPrice = Infinity; 3 | let maxProfit = 0; 4 | 5 | for (let price of prices) { 6 | if (price < minPrice) { 7 | minPrice = price; 8 | } else if (price - minPrice > maxProfit) { 9 | maxProfit = price - minPrice; 10 | } 11 | } 12 | 13 | return maxProfit; 14 | } 15 | 16 | export function maxProfitDBG(){ 17 | 18 | const tests = [ 19 | { 20 | input: [7, 1, 5, 3, 6, 4], 21 | result: 5 // Купить за 1 и продать за 6 22 | }, 23 | { 24 | input: [7, 6, 4, 3, 1], 25 | result: 0 // Прибыль не возможна 26 | } 27 | ]; 28 | 29 | tests.forEach((test, index) => { 30 | const result = maxProfit(test.input); 31 | if (result === test.result) { 32 | console.log(`${index} success`); 33 | } else { 34 | console.log(`${index} fail`); 35 | console.log(`expected ${test.result}`); 36 | console.log(`got ${result}`); 37 | } 38 | }); 39 | } -------------------------------------------------------------------------------- /src/array/prefix_sum/prefix-common-common-array.ts: -------------------------------------------------------------------------------- 1 | function findPrefixCommonArray(A: number[], B: number[]): number[] { 2 | const n = A.length; 3 | const setA = new Set(); 4 | const setB = new Set(); 5 | const result = new Array(n).fill(0); 6 | 7 | for (let i = 0; i < n; i++) { 8 | setA.add(A[i]); 9 | setB.add(B[i]); 10 | 11 | let commonCount = 0; 12 | for (let num of setA) { 13 | if (setB.has(num)) { 14 | commonCount++; 15 | } 16 | } 17 | result[i] = commonCount; 18 | } 19 | return result; 20 | } 21 | 22 | function findThePrefixCommonArray(A: number[], B: number[]): number[] { 23 | const res = []; 24 | const counts = new Array(A.length + 1).fill(0); 25 | let count = 0; 26 | 27 | for (let i = 0; i < A.length; i++) { 28 | const a = A[i]; 29 | const b = B[i]; 30 | if (a === b) { 31 | count += 1; 32 | } else { 33 | count += counts[a] + counts[b]; 34 | counts[a] += 1; 35 | counts[b] += 1; 36 | } 37 | res.push(count); 38 | } 39 | 40 | return res; 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /src/array/prefix_sum/product-except-self.ts: -------------------------------------------------------------------------------- 1 | function productExceptSelf(nums: number[]): number[] { 2 | const answer = new Array(nums.length); 3 | answer[0] = 1; 4 | for (let i = 1; i < nums.length; i++) { 5 | answer[i] = answer[i - 1] * nums[i - 1]; 6 | } 7 | let right = 1; 8 | for (let i = nums.length - 1; i > -1; i--) { 9 | answer[i] = answer[i] * right; 10 | right *= nums[i]; 11 | } 12 | return answer; 13 | }; 14 | 15 | 16 | export function productExceptSelfDBG(){ 17 | const tests = [ 18 | { 19 | input: [1, 2, 3, 4], 20 | result: [24, 12, 8, 6] 21 | }, 22 | { 23 | input: [-1, 1, 0, -3, 3], 24 | result: [0, 0, 9, 0, 0] 25 | } 26 | ]; 27 | 28 | tests.forEach((test, index) => { 29 | const result = productExceptSelf(test.input); 30 | const success = JSON.stringify(result) === JSON.stringify(test.result); 31 | if (success) { 32 | console.log(`${index} success`); 33 | } else { 34 | console.log(`${index} fail`); 35 | console.log(`expected ${JSON.stringify(test.result)}`); 36 | console.log(`got ${JSON.stringify(result)}`); 37 | } 38 | }); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/array/prefix_sum/subarray-sum.ts: -------------------------------------------------------------------------------- 1 | function subarraySum(nums: number[], k: number): number { 2 | const prefixSums = new Map(); 3 | prefixSums.set(0, 1); // Инициализируем, чтобы учитывать сумму от начала массива 4 | 5 | let count = 0; 6 | let sum = 0; 7 | 8 | for (let i = 0; i < nums.length; i++) { 9 | sum += nums[i]; // Текущая сумма от начала массива 10 | 11 | // Если существует такая сумма, что sum - k 12 | // есть в prefixSums, значит найден подмассив 13 | if (prefixSums.has(sum - k)) { 14 | count += prefixSums.get(sum - k)!; 15 | } 16 | 17 | // Обновляем количество префиксных сумм 18 | prefixSums.set(sum, (prefixSums.get(sum) || 0) + 1); 19 | } 20 | 21 | return count; 22 | } 23 | 24 | export function subarraySumDBG(){ 25 | const tests = [ 26 | { 27 | input: { nums: [1, 2, 3], k: 3 }, 28 | result: 2 // Два подмассива: [1,2] (индексы 0-1) и [3] (индекс 2) 29 | }, 30 | { 31 | input: { nums: [1, 1, 1], k: 2 }, 32 | result: 2 // Два подмассива: [1,1] (индексы 0-1) и [1,1] (индексы 1-2) 33 | }, 34 | 35 | ]; 36 | 37 | tests.forEach((test, index) => { 38 | const result = subarraySum(test.input.nums, test.input.k); 39 | const success = result === test.result; 40 | if (success) { 41 | console.log(`${index} success`); 42 | } else { 43 | console.log(`${index} fail`); 44 | console.log(`expected ${test.result}`); 45 | console.log(`got ${result}`); 46 | } 47 | }); 48 | } -------------------------------------------------------------------------------- /src/array/prefix_sum/subarrays-div-by-k.ts: -------------------------------------------------------------------------------- 1 | function subarraysDivByK(nums: number[], k: number): number { 2 | let count = 0; 3 | let prefixSum = 0; 4 | const modCount = new Map(); 5 | modCount.set(0, 1); 6 | 7 | for (const num of nums) { 8 | prefixSum += num; 9 | let mod = prefixSum % k; 10 | if (mod < 0) mod += k; 11 | 12 | if (modCount.has(mod)) { 13 | count += modCount.get(mod)!; 14 | modCount.set(mod, modCount.get(mod)! + 1); 15 | } else { 16 | modCount.set(mod, 1); 17 | } 18 | } 19 | 20 | return count; 21 | } 22 | -------------------------------------------------------------------------------- /src/array/sliding_window/find-max-average.ts: -------------------------------------------------------------------------------- 1 | function findMaxAverage(nums: number[], k: number): number { 2 | let windowSum = 0; 3 | 4 | for (let i = 0; i < k; ++i) { 5 | windowSum += nums[i] 6 | } 7 | let pos1 = 0, highestSum = windowSum; 8 | 9 | for (let i = k; i < nums.length; ++i) { 10 | windowSum = windowSum - nums[pos1++] + nums[i] 11 | highestSum = Math.max(highestSum, windowSum) 12 | } 13 | return highestSum / k 14 | }; -------------------------------------------------------------------------------- /src/array/sliding_window/longest-subarray.ts: -------------------------------------------------------------------------------- 1 | function longestSubarray(nums: number[]): number { 2 | let left = 0; 3 | let right = 0; 4 | let zeroCount = 0; 5 | let maxLen = 0; 6 | 7 | while (right < nums.length) { 8 | if (nums[right] === 0) { 9 | zeroCount++; 10 | } 11 | while (zeroCount > 1) { 12 | if (nums[left] === 0) { 13 | zeroCount--; 14 | } 15 | left++; 16 | } 17 | maxLen = Math.max(maxLen, right - left); 18 | right++; 19 | } 20 | 21 | return maxLen; 22 | } 23 | 24 | 25 | export function longestSubarrayDBG(){ 26 | const tests = [ 27 | { 28 | input: [1, 1, 0, 1], 29 | result: 3 // Удаляем 0, получаем последовательность из трех единиц [1, 1, 1] 30 | }, 31 | { 32 | input: [0, 1, 1, 1, 0, 1, 1, 0, 1], 33 | result: 5 // Удаляем 0 между двумя группами единиц 34 | }, 35 | { 36 | input: [1, 1, 1], 37 | result: 2 // Удаляем одну 1, оставляем две единицы 38 | } 39 | ]; 40 | 41 | tests.forEach((test, index) => { 42 | const result = longestSubarray(test.input); 43 | const success = result === test.result; 44 | if (success) { 45 | console.log(`${index} success`); 46 | } else { 47 | console.log(`${index} fail`); 48 | console.log(`expected ${test.result}`); 49 | console.log(`got ${result}`); 50 | } 51 | }); 52 | } -------------------------------------------------------------------------------- /src/array/sliding_window/max-consecutive-onesII.ts: -------------------------------------------------------------------------------- 1 | function findMaxConsecutiveOnesII(nums: number[]): number { 2 | let left = 0; 3 | let right = 0; 4 | let zeroCount = 0; 5 | let maxConsecutiveOnes = 0; 6 | 7 | while (right < nums.length) { 8 | if (nums[right] === 0) { 9 | zeroCount++; 10 | } 11 | while (zeroCount > 1) { 12 | if (nums[left] === 0) { 13 | zeroCount--; 14 | } 15 | left++; 16 | } 17 | maxConsecutiveOnes = Math.max(maxConsecutiveOnes, right - left + 1); 18 | right++; 19 | } 20 | 21 | return maxConsecutiveOnes; 22 | } 23 | 24 | 25 | function findMaxConsecutiveOnesIIDBG(){ 26 | const tests = [ 27 | { 28 | input: [1, 0, 1, 1, 0], 29 | result: 4 // После замены: [1, 1, 1, 1, 0] -> последовательность из 4 единиц 30 | }, 31 | { 32 | input: [1, 0, 1, 1, 0, 1], 33 | result: 4 // После замены: [1, 1, 1, 1, 0, 1] или [1, 0, 1, 1, 1, 1] 34 | }, 35 | { 36 | input: [0, 0, 0], 37 | result: 1 // После замены одного 0 на 1: [1, 0, 0] -> последовательность из 1 единицы 38 | } 39 | ]; 40 | 41 | tests.forEach((test, index) => { 42 | const result = findMaxConsecutiveOnes(test.input); 43 | const success = result === test.result; 44 | if (success) { 45 | console.log(`${index} success`); 46 | } else { 47 | console.log(`${index} fail`); 48 | console.log(`expected ${test.result}`); 49 | console.log(`got ${result}`); 50 | } 51 | }); 52 | } -------------------------------------------------------------------------------- /src/array/sliding_window/max-consecutive-onesIII.ts: -------------------------------------------------------------------------------- 1 | function findMaxConsecutiveOnesIII(nums: number[], k: number): number { 2 | let left = 0; 3 | let right = 0; 4 | let zeroCount = 0; 5 | let maxLen = 0; 6 | 7 | while (right < nums.length) { 8 | if (nums[right] === 0) { 9 | zeroCount++; 10 | } 11 | while (zeroCount > k) { 12 | if (nums[left] === 0) { 13 | zeroCount--; 14 | } 15 | left++; 16 | } 17 | maxLen = Math.max(maxLen, right - left + 1); 18 | right++; 19 | } 20 | 21 | return maxLen; 22 | } 23 | 24 | 25 | function findMaxConsecutiveOnesIIIDBG(){ 26 | const tests = [ 27 | { 28 | input: { nums: [1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0], k: 2 }, 29 | result: 6 // Максимальная длина подмассива после переворота 2 нулей в единицы 30 | }, 31 | { 32 | input: { nums: [0, 0, 1, 1, 1, 0, 0], k: 0 }, 33 | result: 3 // Максимальная длина подмассива: [1, 1, 1] 34 | } 35 | ]; 36 | 37 | tests.forEach((test, index) => { 38 | const result = findMaxConsecutiveOnesIII(test.input.nums, test.input.k); 39 | const success = result === test.result; 40 | if (success) { 41 | console.log(`${index} success`); 42 | } else { 43 | console.log(`${index} fail`); 44 | console.log(`expected ${test.result}`); 45 | console.log(`got ${result}`); 46 | } 47 | }); 48 | } -------------------------------------------------------------------------------- /src/array/sliding_window/max-dist-to-closet.ts: -------------------------------------------------------------------------------- 1 | function maxDistToClosest(seats: number[]): number { 2 | let maxDistance = 0; 3 | let lastOccupied = -1; 4 | 5 | for (let i = 0; i < seats.length; i++) { 6 | if (seats[i] === 1) { 7 | if (lastOccupied === -1) { 8 | maxDistance = i; 9 | } else { 10 | maxDistance = Math.max(maxDistance, Math.floor((i - lastOccupied) / 2)); 11 | } 12 | lastOccupied = i; 13 | } 14 | } 15 | maxDistance = Math.max(maxDistance, seats.length - 1 - lastOccupied); 16 | return maxDistance; 17 | } 18 | 19 | 20 | function maxDistToClosestDBG(){ 21 | const tests = [ 22 | { 23 | input: [1, 0, 0, 0, 1, 0, 1], 24 | result: 2 // Максимальное расстояние до ближайшего человека: 2 (место 2 или 3) 25 | }, 26 | { 27 | input: [1, 0, 0, 0], 28 | result: 3 // Максимальное расстояние до ближайшего человека: 3 (место 3) 29 | }, 30 | { 31 | input: [0, 1], 32 | result: 1 // Максимальное расстояние до ближайшего человека: 1 (место 0) 33 | } 34 | ]; 35 | 36 | tests.forEach((test, index) => { 37 | const result = maxDistToClosest(test.input); 38 | const success = result === test.result; 39 | if (success) { 40 | console.log(`${index} success`); 41 | } else { 42 | console.log(`${index} fail`); 43 | console.log(`expected ${test.result}`); 44 | console.log(`got ${result}`); 45 | } 46 | }); 47 | } -------------------------------------------------------------------------------- /src/array/sorting/diagonal-sort.ts: -------------------------------------------------------------------------------- 1 | function diagonalSort(mat: number[][]): number[][] { 2 | const m = mat.length; 3 | const n = mat[0].length; 4 | 5 | // Функция для сортировки одной диагонали 6 | const sortDiagonal = (row: number, col: number): void => { 7 | const diagonal: number[] = []; 8 | let r = row; 9 | let c = col; 10 | 11 | // Собираем диагональные элементы 12 | while (r < m && c < n) { 13 | diagonal.push(mat[r][c]); 14 | r++; 15 | c++; 16 | } 17 | 18 | // Сортируем диагональные элементы 19 | diagonal.sort((a, b) => a - b); 20 | 21 | // Записываем отсортированные элементы обратно в диагональ 22 | r = row; 23 | c = col; 24 | let i = 0; 25 | while (r < m && c < n) { 26 | mat[r][c] = diagonal[i]; 27 | r++; 28 | c++; 29 | i++; 30 | } 31 | }; 32 | 33 | // Сортируем диагонали, начинающиеся с каждого элемента первой строки 34 | for (let col = 0; col < n; col++) { 35 | sortDiagonal(0, col); 36 | } 37 | 38 | // Сортируем диагонали, начинающиеся с каждого элемента первого столбца 39 | for (let row = 1; row < m; row++) { 40 | sortDiagonal(row, 0); 41 | } 42 | 43 | return mat; 44 | } 45 | 46 | 47 | export function diagonalSortDBG(){ 48 | const tests = [ 49 | { 50 | input: [[3, 3, 1, 1], [2, 2, 1, 2], [1, 1, 1, 2]], 51 | result: [[1, 1, 1, 1], [1, 2, 2, 2], [1, 2, 3, 3]] 52 | }, 53 | { 54 | input: [[11,25,66,1,69,7],[23,55,17,45,15,52],[75,31,36,44,58,8],[22,27,33,25,68,4],[84,28,14,11,5,50]], 55 | result: [[5,17,4,1,52,7],[11,11,25,45,8,69],[14,23,25,44,58,15],[22,27,31,36,50,66],[84,28,75,33,55,68]] 56 | } 57 | ]; 58 | 59 | tests.forEach((test, index) => { 60 | const result = diagonalSort(test.input); 61 | const success = JSON.stringify(result) === JSON.stringify(test.result); 62 | if (success) { 63 | console.log(`${index} success`); 64 | } else { 65 | console.log(`${index} fail`); 66 | console.log(`expected ${JSON.stringify(test.result)}`); 67 | console.log(`got ${JSON.stringify(result)}`); 68 | } 69 | }); 70 | } -------------------------------------------------------------------------------- /src/array/sorting/merge-sorted-array.ts: -------------------------------------------------------------------------------- 1 | function merge(nums1: number[], m: number, nums2: number[], n: number): void { 2 | let i = m - 1; 3 | let j = n - 1; 4 | let k = m + n - 1; 5 | 6 | while (i >= 0 && j >= 0) { 7 | if (nums1[i] > nums2[j]) { 8 | nums1[k] = nums1[i]; 9 | i--; 10 | } else { 11 | nums1[k] = nums2[j]; 12 | j--; 13 | } 14 | k--; 15 | } 16 | while (j >= 0) { 17 | nums1[k] = nums2[j]; 18 | j--; 19 | k--; 20 | } 21 | } 22 | 23 | function mergeDBG() { 24 | const tests = [ 25 | { 26 | input: { nums1: [1, 2, 3, 0, 0, 0], m: 3, nums2: [2, 5, 6], n: 3 }, 27 | result: [1, 2, 2, 3, 5, 6] 28 | }, 29 | { 30 | input: { nums1: [1], m: 1, nums2: [], n: 0 }, 31 | result: [1] 32 | }, 33 | { 34 | input: { nums1: [0], m: 0, nums2: [1], n: 1 }, 35 | result: [1] 36 | } 37 | ]; 38 | 39 | tests.forEach((test, index) => { 40 | merge(test.input.nums1, test.input.m, test.input.nums2, test.input.n); 41 | const success = JSON.stringify(test.input.nums1) === JSON.stringify(test.result); 42 | if (success) { 43 | console.log(`${index} success`); 44 | } else { 45 | console.log(`${index} fail`); 46 | console.log(`expected ${JSON.stringify(test.result)}`); 47 | console.log(`got ${JSON.stringify(test.input.nums1)}`); 48 | } 49 | }); 50 | } -------------------------------------------------------------------------------- /src/array/two_pointers/3sum-closest.ts: -------------------------------------------------------------------------------- 1 | function threeSumClosest(nums: number[], target: number): number { 2 | 3 | nums.sort((a, b) => a - b); 4 | 5 | let closestSum = nums[0] + nums[1] + nums[2]; 6 | 7 | for (let i = 0; i < nums.length - 2; i++) { 8 | let left = i + 1; 9 | let right = nums.length - 1; 10 | 11 | while (left < right) { 12 | const currentSum = nums[i] + nums[left] + nums[right]; 13 | 14 | if (Math.abs(currentSum - target) < Math.abs(closestSum - target)) { 15 | closestSum = currentSum; 16 | } 17 | 18 | if (currentSum < target) { 19 | left++; 20 | } else if (currentSum > target) { 21 | right--; 22 | } else { 23 | return currentSum; 24 | } 25 | } 26 | } 27 | 28 | return closestSum; 29 | } 30 | -------------------------------------------------------------------------------- /src/array/two_pointers/3sum.ts: -------------------------------------------------------------------------------- 1 | function threeSum(nums: number[]): number[][] { 2 | const result: number[][] = []; 3 | nums.sort((a, b) => a - b); 4 | 5 | for (let i = 0; i < nums.length - 2; i++) { 6 | if (i > 0 && nums[i] === nums[i - 1]) { 7 | continue; 8 | } 9 | 10 | let left = i + 1; 11 | let right = nums.length - 1; 12 | 13 | while (left < right) { 14 | const sum = nums[i] + nums[left] + nums[right]; 15 | if (sum === 0) { 16 | result.push([nums[i], nums[left], nums[right]]); 17 | left++; 18 | right--; 19 | 20 | while (left < right && nums[left] === nums[left - 1]) { 21 | left++; 22 | } 23 | while (left < right && nums[right] === nums[right + 1]) { 24 | right--; 25 | } 26 | } else if (sum < 0) { 27 | left++; 28 | } else { 29 | right--; 30 | } 31 | } 32 | } 33 | 34 | return result; 35 | } 36 | -------------------------------------------------------------------------------- /src/array/two_pointers/find-closest-elements.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | function findClosestElements(arr: number[], k: number, x: number): number[] { 4 | let left = 0; 5 | let right = arr.length - 1; 6 | 7 | while (right - left >= k) { 8 | if (Math.abs(arr[left] - x) > Math.abs(arr[right] - x)) { 9 | left++; 10 | } else { 11 | right--; 12 | } 13 | } 14 | return arr.slice(left, left + k); 15 | } 16 | 17 | export function findClosestElementsDBG(){ 18 | const tests = [ 19 | { 20 | input: { arr: [1, 2, 3, 4, 5, 6, 7, 8, 9], k: 4, x: 3 }, 21 | result: [1, 2, 3, 4] 22 | }, 23 | { 24 | input: { arr: [1, 2, 3, 4, 5], k: 4, x: -1 }, 25 | result: [1, 2, 3, 4] 26 | } 27 | ]; 28 | 29 | tests.forEach((test, index) => { 30 | const result = findClosestElements(test.input.arr, test.input.k, test.input.x); 31 | const success = JSON.stringify(result) === JSON.stringify(test.result); 32 | if (success) { 33 | console.log(`${index} success`); 34 | } else { 35 | console.log(`${index} fail`); 36 | console.log(`expected ${test.result}`); 37 | console.log(`got ${result}`); 38 | } 39 | }); 40 | } -------------------------------------------------------------------------------- /src/array/two_pointers/intersection.ts: -------------------------------------------------------------------------------- 1 | 2 | function intersection(nums1: number[], nums2: number[]): number[] { 3 | 4 | nums1.sort((a, b) => a - b); 5 | nums2.sort((a, b) => a - b); 6 | 7 | const result: number[] = []; 8 | let i = 0; 9 | let j = 0; 10 | 11 | while (i < nums1.length && j < nums2.length) { 12 | if (nums1[i] === nums2[j]) { 13 | if (result.length === 0 || result[result.length - 1] !== nums1[i]) { 14 | result.push(nums1[i]); 15 | } 16 | i++; 17 | j++; 18 | } else if (nums1[i] < nums2[j]) { 19 | i++; 20 | } else { 21 | j++; 22 | } 23 | } 24 | return result; 25 | } 26 | 27 | 28 | 29 | export function intersectionDBG() { 30 | const tests = [ 31 | { 32 | input: { nums1: [1, 2, 2, 1], nums2: [2, 2] }, 33 | result: [2] 34 | }, 35 | { 36 | input: { nums1: [4, 9, 5], nums2: [9, 4, 9, 8, 4] }, 37 | result: [4, 9] 38 | } 39 | ]; 40 | 41 | tests.forEach((test, index) => { 42 | const result = intersection(test.input.nums1, test.input.nums2); 43 | const success = JSON.stringify(result.sort()) === JSON.stringify(test.result.sort()); 44 | if (success) { 45 | console.log(`${index} success`); 46 | } else { 47 | console.log(`${index} fail`); 48 | console.log(`expected ${JSON.stringify(test.result)}`); 49 | console.log(`got ${JSON.stringify(result)}`); 50 | } 51 | }); 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/array/two_pointers/move-zeros.ts: -------------------------------------------------------------------------------- 1 | function moveZeroes(nums: number[]): void { 2 | let lastNonZeroFoundAt = 0; 3 | 4 | for (let i = 0; i < nums.length; i++) { 5 | if (nums[i] !== 0) { 6 | nums[lastNonZeroFoundAt] = nums[i]; 7 | lastNonZeroFoundAt++; 8 | } 9 | } 10 | 11 | for (let i = lastNonZeroFoundAt; i < nums.length; i++) { 12 | nums[i] = 0; 13 | } 14 | } 15 | 16 | function moveZeroesDBG(){ 17 | const tests = [ 18 | { 19 | input: [0, 1, 0, 3, 12], 20 | result: [1, 3, 12, 0, 0] 21 | }, 22 | { 23 | input: [0, 0, 1], 24 | result: [1, 0, 0] 25 | } 26 | ]; 27 | 28 | tests.forEach((test, index) => { 29 | moveZeroes(test.input); 30 | const success = JSON.stringify(test.input) === JSON.stringify(test.result); 31 | if (success) { 32 | console.log(`${index} success`); 33 | } else { 34 | console.log(`${index} fail`); 35 | console.log(`expected ${JSON.stringify(test.result)}`); 36 | console.log(`got ${JSON.stringify(test.input)}`); 37 | } 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /src/array/two_pointers/remove-zeros.ts: -------------------------------------------------------------------------------- 1 | export function removeZeros(arr: number[]): number[] { 2 | let writeIndex = 0; 3 | for (let i = 0; i < arr.length; i++) { 4 | if (arr[i] !== 0) { 5 | arr[writeIndex] = arr[i]; 6 | writeIndex++; 7 | } 8 | } 9 | arr.length = writeIndex; 10 | return arr; 11 | } -------------------------------------------------------------------------------- /src/array/two_pointers/reverse-vowels.ts: -------------------------------------------------------------------------------- 1 | const reverseVowels = (s: string) => { 2 | const vowelSet = new Set([ 3 | 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U', 4 | ]) 5 | const str = Array.from(s); 6 | let l = 0; 7 | let r = s.length - 1; 8 | 9 | while (l < r) { 10 | while (!vowelSet.has(str[l]) && l < r) l++; 11 | while (!vowelSet.has(str[r]) && l < r) r--; 12 | if (l < r) { 13 | [str[l++], str[r--]] = [str[r], str[l]] 14 | } 15 | } 16 | return str.join('') 17 | }; -------------------------------------------------------------------------------- /src/array/two_pointers/sorted-squares.ts: -------------------------------------------------------------------------------- 1 | function sortedSquares(nums: number[]): number[] { 2 | const result = new Array(nums.length); 3 | let left = 0; 4 | let right = nums.length - 1; 5 | let index = nums.length - 1; 6 | 7 | while (left <= right) { 8 | const leftSquare = nums[left] * nums[left]; 9 | const rightSquare = nums[right] * nums[right]; 10 | 11 | if (leftSquare > rightSquare) { 12 | result[index] = leftSquare; 13 | left++; 14 | } else { 15 | result[index] = rightSquare; 16 | right--; 17 | } 18 | 19 | index--; 20 | } 21 | return result; 22 | } 23 | -------------------------------------------------------------------------------- /src/array/two_pointers/trapping-rain-water.ts: -------------------------------------------------------------------------------- 1 | function trap(height: number[]): number { 2 | let left = 0; 3 | let right = height.length - 1; 4 | let leftMax = 0; 5 | let rightMax = 0; 6 | let waterTrapped = 0; 7 | 8 | while (left < right) { 9 | if (height[left] < height[right]) { 10 | if (height[left] >= leftMax) { 11 | leftMax = height[left]; 12 | } else { 13 | waterTrapped += leftMax - height[left]; 14 | } 15 | left++; 16 | } else { 17 | if (height[right] >= rightMax) { 18 | rightMax = height[right]; 19 | } else { 20 | waterTrapped += rightMax - height[right]; 21 | } 22 | right--; 23 | } 24 | } 25 | 26 | return waterTrapped; 27 | } 28 | 29 | 30 | export function trapDBG() { 31 | const tests = [ 32 | { 33 | // picture 34 | input: [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1], 35 | result: 6 36 | }, 37 | { 38 | input: [4, 2, 0, 3, 2, 5], 39 | result: 9 40 | }, 41 | { 42 | input: [1, 1], 43 | result: 0 44 | } 45 | ]; 46 | 47 | tests.forEach((test, index) => { 48 | const result = trap(test.input); 49 | if (result === test.result) { 50 | console.log(`${index} success`); 51 | } else { 52 | console.log(`${index} fail`); 53 | console.log(`expected ${test.result}`); 54 | console.log(`got ${result}`); 55 | } 56 | }); 57 | } -------------------------------------------------------------------------------- /src/array/two_pointers/two-sum.ts: -------------------------------------------------------------------------------- 1 | function twoSum(nums: number[], target: number): number[] { 2 | let left = 0; 3 | let right = nums.length - 1; 4 | 5 | while (left < right) { 6 | const sum = nums[left] + nums[right]; 7 | 8 | if (sum === target) { 9 | return [left, right]; 10 | } else if (sum < target) { 11 | left++; 12 | } else { 13 | right--; 14 | } 15 | } 16 | return []; 17 | } 18 | -------------------------------------------------------------------------------- /src/bst/bfs-all-paths.task.ts: -------------------------------------------------------------------------------- 1 | import { Graph } from "src/graph/utilities"; 2 | 3 | export function bfsAllPaths(graph: Graph, start: string, end: string): string[][] { 4 | const paths: string[][] = []; 5 | const queue: string[][] = [[start]]; 6 | 7 | while (queue.length > 0) { 8 | const path = queue.shift(); 9 | const node = path[path.length - 1]; 10 | 11 | if (node === end) { 12 | paths.push(path); 13 | } 14 | 15 | const neighbors = graph[node]; 16 | for (const neighbor of neighbors) { 17 | if(!path.includes(neighbor)){ 18 | queue.push([...path, neighbor]); 19 | } 20 | } 21 | } 22 | 23 | return paths; 24 | } -------------------------------------------------------------------------------- /src/bst/bst-max-path-sum.ts: -------------------------------------------------------------------------------- 1 | import { printTree, TreeNode } from "./utilities"; 2 | 3 | 4 | function maxPathSum(root: TreeNode | null): number { 5 | let maxSum = -Infinity; 6 | 7 | const maxGain = (node: TreeNode | null): number => { 8 | if (!node) return 0; 9 | 10 | const leftGain = Math.max(maxGain(node.left), 0); 11 | const rightGain = Math.max(maxGain(node.right), 0); 12 | 13 | const currentPathSum = node.val + leftGain + rightGain; 14 | 15 | maxSum = Math.max(maxSum, currentPathSum); 16 | 17 | return node.val + Math.max(leftGain, rightGain); 18 | }; 19 | 20 | maxGain(root); 21 | return maxSum; 22 | } 23 | 24 | 25 | export function maxPathSumDBG() { 26 | const tests = [ 27 | { 28 | input: new TreeNode(1, new TreeNode(2), new TreeNode(3)), 29 | result: 6 30 | }, 31 | { 32 | input: new TreeNode(-10, new TreeNode(9), new TreeNode(20, new TreeNode(15), new TreeNode(7))), 33 | result: 42 34 | }, 35 | { 36 | input: new TreeNode(2, new TreeNode(-1)), 37 | result: 2 38 | } 39 | ]; 40 | 41 | tests.forEach((test, index) => { 42 | const result = maxPathSum(test.input); 43 | if (result === test.result) { 44 | console.log(`${index} success`); 45 | } else { 46 | console.log(`${index} fail`); 47 | console.log(`expected ${test.result}`); 48 | console.log(`got ${result}`); 49 | } 50 | printTree(test.input); 51 | console.log() 52 | }); 53 | } -------------------------------------------------------------------------------- /src/bst/bst-side-view.ts: -------------------------------------------------------------------------------- 1 | import { printTree, TreeNode } from "./utilities"; 2 | 3 | function rightSideView(root: TreeNode | null): number[] { 4 | const result: number[] = []; 5 | if (!root) return result; 6 | 7 | const queue: TreeNode[] = [root]; 8 | 9 | while (queue.length > 0) { 10 | const levelSize = queue.length; 11 | for (let i = 0; i < levelSize; i++) { 12 | const node = queue.shift()!; 13 | 14 | if (i === levelSize - 1) { 15 | result.push(node.val); 16 | } 17 | 18 | if (node.left) queue.push(node.left); 19 | if (node.right) queue.push(node.right); 20 | } 21 | } 22 | return result; 23 | } 24 | 25 | export function rightSideViewDBG(){ 26 | 27 | const tests = [ 28 | { 29 | input: new TreeNode(1, new TreeNode(2, null, new TreeNode(5)), new TreeNode(3, null, new TreeNode(4))), 30 | result: [1, 3, 4] 31 | }, 32 | { 33 | input: new TreeNode(1, new TreeNode(2, new TreeNode(4)), new TreeNode(3)), 34 | result: [1, 3, 4] 35 | }, 36 | { 37 | input: null, 38 | result: [] 39 | } 40 | ]; 41 | 42 | tests.forEach((test, index) => { 43 | const result = rightSideView(test.input); 44 | if (JSON.stringify(result) === JSON.stringify(test.result)) { 45 | console.log(`${index} success`); 46 | } else { 47 | console.log(`${index} fail`); 48 | console.log(`expected ${test.result}`); 49 | console.log(`got ${result}`); 50 | } 51 | printTree(test.input) 52 | }); 53 | } -------------------------------------------------------------------------------- /src/bst/delete.ts: -------------------------------------------------------------------------------- 1 | import { printTree, TreeNode } from "./utilities"; 2 | 3 | function deleteNode(root: TreeNode | null, key: number): TreeNode | null { 4 | if (!root) { 5 | return null; 6 | } 7 | if (key < root.val) { 8 | root.left = deleteNode(root.left, key); 9 | } 10 | else if (key > root.val) { 11 | root.right = deleteNode(root.right, key); 12 | } 13 | else { 14 | if (!root.left) { 15 | return root.right; 16 | } 17 | else if (!root.right) { 18 | return root.left; 19 | } 20 | 21 | let minNode = root.right; 22 | while (minNode.left) { 23 | minNode = minNode.left; 24 | } 25 | root.val = minNode.val; 26 | root.right = deleteNode(root.right, minNode.val); 27 | } 28 | return root; 29 | } 30 | 31 | function deleteNodeDBG() { 32 | const tests = [ 33 | { 34 | input: { root: new TreeNode(5, new TreeNode(3, new TreeNode(2), new TreeNode(4)), new TreeNode(6, null, new TreeNode(7))), key: 3 }, 35 | result: new TreeNode(5, new TreeNode(4, new TreeNode(2)), new TreeNode(6, null, new TreeNode(7))) 36 | }, 37 | { 38 | input: { root: new TreeNode(5, new TreeNode(3), new TreeNode(6, null, new TreeNode(7))), key: 5 }, 39 | result: new TreeNode(6, new TreeNode(3), new TreeNode(7)) 40 | } 41 | ]; 42 | 43 | tests.forEach((test, index) => { 44 | console.log(`init tree`) 45 | printTree(test.input.root) 46 | const result = deleteNode(test.input.root, test.input.key); 47 | const success = JSON.stringify(result) === JSON.stringify(test.result); 48 | if (success) { 49 | console.log(`result`) 50 | printTree(test.input.root) 51 | console.log(`${index} success`); 52 | } else { 53 | console.log(`${index} fail`); 54 | console.log(`expected ${test.result}`); 55 | console.log(`got ${result}`); 56 | } 57 | console.log(`-------------------`) 58 | }); 59 | } -------------------------------------------------------------------------------- /src/bst/find-min.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from "./utilities"; 2 | 3 | function findMin(root: TreeNode | null): number | null { 4 | if (!root) { 5 | return null; 6 | } 7 | while (root.left) { 8 | root = root.left; 9 | } 10 | return root.val; 11 | } 12 | -------------------------------------------------------------------------------- /src/bst/insert.ts: -------------------------------------------------------------------------------- 1 | import { printTree, TreeNode } from "./utilities"; 2 | 3 | function insertIntoBST(root: TreeNode | null, val: number): TreeNode | null { 4 | if (!root) { 5 | return new TreeNode(val); 6 | } 7 | if (val < root.val) { 8 | root.left = insertIntoBST(root.left, val); 9 | } else { 10 | root.right = insertIntoBST(root.right, val); 11 | } 12 | return root; 13 | } 14 | 15 | 16 | function testInsertIntoBST() { 17 | const tests = [ 18 | { 19 | input: { 20 | root: new TreeNode(4, new TreeNode(2), new TreeNode(7)), 21 | val: 5 22 | }, 23 | result: new TreeNode(4, new TreeNode(2), new TreeNode(7, new TreeNode(5))) 24 | }, 25 | { 26 | input: { 27 | root: new TreeNode(4, new TreeNode(2), new TreeNode(7)), 28 | val: 1 29 | }, 30 | result: new TreeNode(4, new TreeNode(2, new TreeNode(1)), new TreeNode(7)) 31 | } 32 | ]; 33 | 34 | tests.forEach((test, index) => { 35 | const result = insertIntoBST(test.input.root, test.input.val); 36 | const success = JSON.stringify(result) === JSON.stringify(test.result); 37 | if (success) { 38 | console.log(`${index} success`); 39 | } else { 40 | console.log(`${index} fail`); 41 | console.log(`expected ${test.result}`); 42 | console.log(`got ${result}`); 43 | } 44 | printTree(result) 45 | }); 46 | 47 | } -------------------------------------------------------------------------------- /src/bst/is-balanced.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from "./utilities"; 2 | 3 | function isBalanced(root: TreeNode | null): boolean { 4 | const checkBalance = (node: TreeNode | null): number => { 5 | if (!node) { 6 | return 0; 7 | } 8 | const leftHeight = checkBalance(node.left); 9 | if (leftHeight === -1) { 10 | return -1; 11 | } 12 | const rightHeight = checkBalance(node.right); 13 | if (rightHeight === -1) { 14 | return -1; 15 | } 16 | if (Math.abs(leftHeight - rightHeight) > 1) { 17 | return -1; 18 | } 19 | return Math.max(leftHeight, rightHeight) + 1; 20 | } 21 | return checkBalance(root) !== -1; 22 | } 23 | -------------------------------------------------------------------------------- /src/bst/is-symmetric.ts: -------------------------------------------------------------------------------- 1 | import { printTree, TreeNode } from "./utilities"; 2 | 3 | function isSymmetric(root: TreeNode | null): boolean { 4 | if (!root) return true; 5 | const isMirror = (t1: TreeNode, t2: TreeNode): boolean => { 6 | if (t1 === null && t2 === null) return true; 7 | if (t1 === null || t2 === null) return false; 8 | 9 | return (t1.val === t2.val) 10 | && isMirror(t1.left, t2.right) 11 | && isMirror(t1.right, t2.left); 12 | } 13 | return isMirror(root, root); 14 | }; 15 | -------------------------------------------------------------------------------- /src/bst/is-valid-bst.ts: -------------------------------------------------------------------------------- 1 | import { printTree, TreeNode } from "./utilities"; 2 | 3 | function isValidBST(root: TreeNode | null, min?: number, max?: number): boolean { 4 | const currentVal = root?.val; 5 | const leftVal = root?.left?.val; 6 | const rightVal = root?.right?.val; 7 | 8 | if (leftVal !== undefined) { 9 | if (currentVal! <= leftVal) return false; 10 | if (min && min >= leftVal) return false; 11 | if (root?.left && !isValidBST(root.left, min, currentVal)) return false; 12 | } 13 | if (rightVal !== undefined) { 14 | if (currentVal! >= rightVal) return false; 15 | if (max && max <= rightVal) return false; 16 | if (root!.right && !isValidBST(root!.right, currentVal, max)) return false; 17 | } 18 | 19 | return true; 20 | } 21 | 22 | export function isValidBSTDBG(){ 23 | const tests = [ 24 | { 25 | input: new TreeNode(2, new TreeNode(1), new TreeNode(3)), 26 | result: true 27 | }, 28 | { 29 | input: new TreeNode(5, new TreeNode(1), new TreeNode(4, new TreeNode(3), new TreeNode(6))), 30 | result: false 31 | }, 32 | { 33 | input: null, 34 | result: true 35 | } 36 | ]; 37 | 38 | tests.forEach((test, index) => { 39 | const result = isValidBST(test.input); 40 | if (result === test.result) { 41 | console.log(`${index} success got ${result}`); 42 | } else { 43 | console.log(`${index} fail`); 44 | console.log(`expected ${test.result}`); 45 | console.log(`got ${result}`); 46 | } 47 | printTree(test.input); 48 | console.log() 49 | }); 50 | } -------------------------------------------------------------------------------- /src/bst/leaf-similar.ts: -------------------------------------------------------------------------------- 1 | import { createBinaryTree, TreeNode } from "./utilities"; 2 | 3 | /** 4 | * @problem 5 | * [872. Leaf-Similar Trees](https://leetcode.com/problems/leaf-similar-trees) 6 | */ 7 | function leafSimilar(root1: TreeNode, root2: TreeNode): boolean { 8 | const getLeafs = (root: TreeNode): number[] => { 9 | if (!root.left && !root.right) { 10 | return [root.val]; 11 | } 12 | const left = root.left ? getLeafs(root.left) : []; 13 | const right = root.right ? getLeafs(root.right) : []; 14 | return [...left, ...right]; 15 | } 16 | const root1Leafs = getLeafs(root1).join('#'); 17 | const root2Leafs = getLeafs(root2).join('#'); 18 | return root1Leafs === root2Leafs; 19 | }; 20 | 21 | export function leafSimilarDBG() { 22 | const tests = [ 23 | { 24 | input: { 25 | root1: createBinaryTree([3, 5, 1, 6, 2, 9, 8, null, null, 7, 4]), 26 | root2: createBinaryTree([3, 5, 1, 6, 7, 4, 2, null, null, null, null, null, null, 9, 8]) 27 | }, 28 | result: true 29 | }, 30 | { 31 | input: { 32 | root1: createBinaryTree([1, 2, 3]), 33 | root2: createBinaryTree([1, 3, 2]) 34 | }, 35 | result: false 36 | }, 37 | { 38 | input: { 39 | root1: createBinaryTree([1]), 40 | root2: createBinaryTree([1]) 41 | }, 42 | result: true 43 | }, 44 | ]; 45 | 46 | tests.forEach((test, index) => { 47 | const output = leafSimilar(test.input.root1, test.input.root2); 48 | const success = output === test.result; 49 | if (success) { 50 | console.log(`${index} success`); 51 | } else { 52 | console.log(`${index} fail`); 53 | console.log(`expected ${test.result}`); 54 | console.log(`got ${output}`); 55 | } 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /src/bst/lowest-common-ancestor.ts: -------------------------------------------------------------------------------- 1 | import { printTree, TreeNode } from "./utilities"; 2 | 3 | function lowestCommonAncestor(root: TreeNode | null, p: TreeNode | null, q: TreeNode | null): TreeNode | null { 4 | if (root === null || root === p || root === q) { 5 | return root; 6 | } 7 | 8 | const left = lowestCommonAncestor(root.left, p, q); 9 | const right = lowestCommonAncestor(root.right, p, q); 10 | 11 | if (left !== null && right !== null) { 12 | return root; 13 | } 14 | 15 | return left !== null ? left : right; 16 | } 17 | 18 | 19 | export function lowestCommonAncestorDBG() { 20 | const root = new TreeNode(3, 21 | new TreeNode(5, 22 | new TreeNode(6), 23 | new TreeNode(2, new TreeNode(7), new TreeNode(4)) 24 | ), 25 | new TreeNode(1, new TreeNode(0), new TreeNode(8)) 26 | ); 27 | 28 | const tests = [ 29 | { 30 | input: { 31 | root, 32 | p: root.left, // Узел с значением 5 33 | q: root.right // Узел с значением 1 34 | }, 35 | result: 3 36 | }, 37 | { 38 | input: { 39 | root, 40 | p: root.left, // Узел с значением 5 41 | q: root.left?.right?.right // Узел с значением 4 42 | }, 43 | result: 5 44 | } 45 | ]; 46 | 47 | tests.forEach((test, index) => { 48 | const result = lowestCommonAncestor(test.input.root, test.input.p, test.input.q); 49 | if (result?.val === test.result) { 50 | console.log(`${index} success`); 51 | } else { 52 | console.log(`${index} fail`); 53 | console.log(`expected ${test.result}`); 54 | console.log(`got ${result?.val}`); 55 | } 56 | printTree(root) 57 | }); 58 | } -------------------------------------------------------------------------------- /src/bst/serialize-and-deserialize.ts: -------------------------------------------------------------------------------- 1 | import { printTree, TreeNode } from "./utilities"; 2 | 3 | function serialize(root: TreeNode): string { 4 | const preorderSerialize = (node: TreeNode) => { 5 | if (!node) return []; 6 | return [ 7 | node.val.toString(), 8 | ...preorderSerialize(node.left), 9 | ...preorderSerialize(node.right) 10 | ] 11 | } 12 | return preorderSerialize(root).join(',') 13 | }; 14 | 15 | 16 | function deserialize(data: string): TreeNode | null { 17 | if (!data) return null; 18 | const values = data.split(',').map(Number); 19 | 20 | const buildBST = (values: number[], bound: number) => { 21 | if (values.length === 0 || values[0] > bound) return null; 22 | const val = values.shift()!; 23 | const node = new TreeNode(val); 24 | node.left = buildBST(values, val); 25 | node.right = buildBST(values, bound); 26 | return node; 27 | } 28 | return buildBST(values, Infinity); 29 | } 30 | 31 | export function TTTDBG() { 32 | 33 | const tests = [ 34 | { 35 | input: new TreeNode( 36 | 2, 37 | new TreeNode(1), 38 | new TreeNode(3) 39 | ), 40 | serialized: "2,1,3", 41 | }, 42 | { 43 | input: new TreeNode( 44 | 5, 45 | new TreeNode(3, new TreeNode(2), new TreeNode(4)), 46 | new TreeNode(7, null, new TreeNode(8)) 47 | ), 48 | serialized: "5,3,7,2,4,8", 49 | }, 50 | { 51 | input: null, 52 | serialized: "", 53 | } 54 | ]; 55 | 56 | tests.forEach((test, index) => { 57 | const serialized = serialize(test.input); 58 | const successSerialize = serialized === test.serialized; 59 | console.log(`Test ${index} Serialize: ${successSerialize ? "success" : "fail"}`); 60 | if (!successSerialize) { 61 | console.log(`expected: ${test.serialized}`); 62 | console.log(`got: ${serialized}`); 63 | } 64 | 65 | const deserialized = deserialize(serialized); 66 | const reSerialized = serialize(deserialized); 67 | const successDeserialize = reSerialized === test.serialized; 68 | console.log(`Test ${index} Deserialize: ${successDeserialize ? "success" : "fail"}`); 69 | if (!successDeserialize) { 70 | console.log(`expected: ${test.serialized}`); 71 | console.log(`got: ${reSerialized}`); 72 | } 73 | }); 74 | } -------------------------------------------------------------------------------- /src/bst/sum-of-left-leaves.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from "./utilities"; 2 | 3 | function sumOfLeftLeaves(root: TreeNode | null): number { 4 | let result = 0; 5 | const traversal = (node: TreeNode, isLeft: boolean) => { 6 | if (!node) return 0; 7 | 8 | result += isLeft && !node?.left && !node?.right ? node.val : 0; 9 | 10 | traversal(node.left, true); 11 | traversal(node.right, false); 12 | } 13 | traversal(root, false); 14 | return result; 15 | 16 | }; -------------------------------------------------------------------------------- /src/bst/utilities/bst.structure.ts: -------------------------------------------------------------------------------- 1 | export class TreeNode { 2 | public val: number; 3 | public left: TreeNode | null; 4 | public right: TreeNode | null; 5 | 6 | constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { 7 | this.val = val === undefined ? 0 : val; 8 | this.left = left === undefined ? null : left; 9 | this.right = right === undefined ? null : right; 10 | } 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/bst/utilities/bst.utilities.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | import { TreeNode } from "."; 3 | 4 | 5 | function _printTree( 6 | node: TreeNode | null, 7 | prefix: string = "", 8 | isRight: boolean = true, 9 | hasLeftSibling: boolean = false 10 | ): void { 11 | if (node === null) return; 12 | const currentPrefix = prefix + (isRight ? (hasLeftSibling ? "├── " : "└── ") : hasLeftSibling == isRight ? "└── ": "├── "); 13 | console.log(currentPrefix + node.val); 14 | const childPrefix = prefix + (hasLeftSibling ? "│ " : " "); 15 | if (node.left || node.right) { 16 | if (node.right) { 17 | _printTree(node.right, childPrefix, true, node.left !== null); 18 | } 19 | if (node.left) { 20 | _printTree(node.left, childPrefix, false, false); 21 | } 22 | } 23 | } 24 | 25 | export function printTree(node: TreeNode): void { 26 | if (node === null) { 27 | console.log(`BST null`) 28 | return; 29 | }; 30 | console.log('BST ' + node.val); 31 | const childPrefix = " "; 32 | if (node.left || node.right) { 33 | if (node.right) { 34 | _printTree(node.right, childPrefix, true, node.left !== null); 35 | } 36 | if (node.left) { 37 | _printTree(node.left, childPrefix, false, false); 38 | } 39 | } 40 | } 41 | 42 | export function createBinaryTree(values: (number | null)[]): TreeNode | null { 43 | if (values.length === 0 || values[0] == null) return null; 44 | 45 | const root = new TreeNode(values[0]); 46 | const queue: (TreeNode | null)[] = [root]; 47 | let i = 1; 48 | 49 | while (i < values.length) { 50 | const current = queue.shift(); 51 | if (!current) continue; 52 | 53 | const leftVal = values[i++]; 54 | if (leftVal != null) { 55 | current.left = new TreeNode(leftVal); 56 | queue.push(current.left); 57 | } 58 | 59 | if (i >= values.length) break; 60 | 61 | const rightVal = values[i++]; 62 | if (rightVal != null) { 63 | current.right = new TreeNode(rightVal); 64 | queue.push(current.right); 65 | } 66 | } 67 | 68 | return root; 69 | } -------------------------------------------------------------------------------- /src/bst/utilities/index.ts: -------------------------------------------------------------------------------- 1 | export * from './bst.structure'; 2 | export * from './bst.utilities'; -------------------------------------------------------------------------------- /src/graph/alien-order.ts: -------------------------------------------------------------------------------- 1 | // input: ["wrt", "wrf", "er", "ett", "rftt"], 2 | // expected: "wertf" 3 | 4 | function alienOrder(words: string[]): string { 5 | const graph: Record> = Object.create(null); 6 | const inDegree: Record = Object.create(null); 7 | 8 | for (const word of words) { 9 | for (const char of word) { 10 | if (!graph[char]) { 11 | graph[char] = new Set(); 12 | inDegree[char] = 0; 13 | } 14 | } 15 | } 16 | for (let i = 0; i < words.length - 1; i++) { 17 | const word1 = words[i]; 18 | const word2 = words[i + 1]; 19 | //"wrt", "wrf" 20 | const minLength = Math.min(word1.length, word2.length); 21 | let foundDifference = false; 22 | 23 | for (let j = 0; j < minLength; j++) { 24 | if (word1[j] !== word2[j]) { 25 | if (!graph[word1[j]].has(word2[j])) { 26 | graph[word1[j]].add(word2[j]); 27 | inDegree[word2[j]]++; 28 | } 29 | foundDifference = true; 30 | break; 31 | } 32 | } 33 | if (!foundDifference && word1.length > word2.length) { 34 | return ""; 35 | } 36 | } 37 | const queue: string[] = []; 38 | const order: string[] = []; 39 | for (const char in inDegree) { 40 | if (inDegree[char] === 0) { 41 | queue.push(char); 42 | } 43 | } 44 | while (queue.length > 0) { 45 | const char = queue.shift()!; 46 | order.push(char); 47 | for (const neighbor of graph[char]) { 48 | inDegree[neighbor]--; 49 | if (inDegree[neighbor] === 0) { 50 | queue.push(neighbor); 51 | } 52 | } 53 | } 54 | if (order.length === Object.keys(inDegree).length) { 55 | return order.join(""); 56 | } else { 57 | return ""; 58 | } 59 | } 60 | 61 | 62 | export function alienOrderDBG(){ 63 | const tests = [ 64 | { 65 | input: ["wrt", "wrf", "er", "ett", "rftt"], 66 | expected: "wertf" 67 | }, 68 | { 69 | input: ["z", "x"], 70 | expected: "zx" 71 | }, 72 | { 73 | input: ["z", "x", "z"], 74 | expected: "" // Цикл в графе — нет корректного порядка 75 | } 76 | ]; 77 | 78 | tests.forEach((test, index) => { 79 | const result = alienOrder(test.input); 80 | const success = result === test.expected; 81 | if (success) { 82 | console.log(`Test ${index} success`); 83 | } else { 84 | console.log(`Test ${index} fail`); 85 | console.log(`expected: ${test.expected}`); 86 | console.log(`got: ${result}`); 87 | } 88 | }); 89 | 90 | } -------------------------------------------------------------------------------- /src/graph/calc-equation.ts: -------------------------------------------------------------------------------- 1 | function calcEquation( 2 | equations: string[][], 3 | values: number[], 4 | queries: string[][] 5 | ): number[] { 6 | const graph = new Map>(); 7 | const result: number[] = []; 8 | 9 | for (let i = 0; i < equations.length; i++) { 10 | const [a, b] = equations[i]; 11 | const value = values[i]; 12 | 13 | if (!graph.has(a)) graph.set(a, new Map()); 14 | if (!graph.has(b)) graph.set(b, new Map()); 15 | 16 | graph.get(a).set(b, value); 17 | graph.get(b).set(a, 1/value); 18 | } 19 | 20 | const dfs = (start: string, end: string, visited: Set): number => { 21 | if (!graph.has(start) || !graph.has(end)) { 22 | return -1.0; 23 | } 24 | if (start === end) { 25 | return 1.0; 26 | } 27 | visited.add(start); 28 | 29 | for (const [neighbor, neighborVal] of graph.get(start)) { 30 | if (!visited.has(neighbor)) { 31 | const result = dfs(neighbor, end, visited); 32 | if (result !== -1.0) { 33 | return result * neighborVal 34 | } 35 | } 36 | } 37 | return -1.0; 38 | } 39 | 40 | for (const [d, c] of queries) { 41 | const visited = new Set(); 42 | result.push(dfs(d, c, visited)); 43 | } 44 | 45 | return result; 46 | }; 47 | 48 | export function calcEquationDBG() { 49 | const tests = [ 50 | { 51 | input: { 52 | equations: [["a", "b"], ["b", "c"]], 53 | values: [2.0, 3.0], 54 | queries: [["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"]] 55 | }, 56 | expected: [6.0, 0.5, -1.0, 1.0, -1.0] 57 | }, 58 | { 59 | input: { 60 | equations: [["a", "b"], ["b", "c"], ["bc", "cd"]], 61 | values: [1.5, 2.5, 5.0], 62 | queries: [["a", "c"], ["c", "b"], ["bc", "cd"], ["cd", "bc"]] 63 | }, 64 | expected: [3.75, 0.4, 5.0, 0.2] 65 | }, 66 | { 67 | input: { 68 | equations: [["a", "b"]], 69 | values: [0.5], 70 | queries: [["a", "b"], ["b", "a"], ["a", "c"], ["x", "y"]] 71 | }, 72 | expected: [0.5, 2.0, -1.0, -1.0] 73 | } 74 | ]; 75 | 76 | tests.forEach((test, index) => { 77 | const result = calcEquation(test.input.equations, test.input.values, test.input.queries); 78 | const success = JSON.stringify(result) === JSON.stringify(test.expected); 79 | if (success) { 80 | console.log(`Test ${index} success`); 81 | } else { 82 | console.log(`Test ${index} fail`); 83 | console.log(`expected: ${JSON.stringify(test.expected)}`); 84 | console.log(`got: ${JSON.stringify(result)}`); 85 | } 86 | }); 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/graph/find-iternarary.ts: -------------------------------------------------------------------------------- 1 | function findItinerary(tickets: string[][]): string[] { 2 | const adjList: { [key: string]: string[] } = {}; 3 | 4 | for (let [from, to] of tickets) { 5 | if (!adjList[from]) adjList[from] = []; 6 | adjList[from].push(to); 7 | } 8 | 9 | for (let city in adjList) { 10 | adjList[city].sort().reverse(); 11 | } 12 | 13 | const result: string[] = []; 14 | 15 | const dfs = (node: string) => { 16 | const destinations = adjList[node]; 17 | while (destinations && destinations.length > 0) { 18 | dfs(destinations.pop()!); 19 | } 20 | result.push(node); 21 | }; 22 | 23 | dfs("JFK"); 24 | 25 | return result.reverse(); 26 | } 27 | 28 | 29 | export function findItineraryDBG(){ 30 | const tests = [ 31 | { 32 | input: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]], 33 | result: ["JFK", "MUC", "LHR", "SFO", "SJC"] 34 | }, 35 | { 36 | input: [["JFK", "SFO"], ["JFK", "ATL"], ["SFO", "ATL"], ["ATL", "JFK"], ["ATL", "SFO"]], 37 | result: ["JFK", "ATL", "JFK", "SFO", "ATL", "SFO"] 38 | } 39 | ]; 40 | 41 | tests.forEach((test, index) => { 42 | const result = findItinerary(test.input); 43 | const success = JSON.stringify(result) === JSON.stringify(test.result); 44 | if (success) { 45 | console.log(`${index} success`); 46 | } else { 47 | console.log(`${index} fail`); 48 | console.log(`expected ${JSON.stringify(test.result)}`); 49 | console.log(`got ${JSON.stringify(result)}`); 50 | } 51 | }); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/graph/network-delay-time.ts: -------------------------------------------------------------------------------- 1 | function networkDelayTime( 2 | times: number[][], 3 | n: number, 4 | k: number 5 | ): number { 6 | // Граф: узел -> список [сосед, вес] 7 | const graph: Record = {}; 8 | 9 | // Построение графа 10 | for (const [u, v, w] of times) { 11 | if (!graph[u]) graph[u] = []; 12 | graph[u].push([v, w]); 13 | } 14 | 15 | // Инициализация расстояний 16 | const dist = new Array(n + 1).fill(Infinity); 17 | dist[k] = 0; 18 | 19 | // Очередь для обработки узлов (минимальная куча) 20 | // [время, узел] 21 | const pq: [number, number][] = [[0, k]]; 22 | 23 | while (pq.length > 0) { 24 | // Сортировка по времени 25 | pq.sort((a, b) => a[0] - b[0]); 26 | const [time, node] = pq.shift()!; 27 | 28 | // Если время больше уже вычисленного, пропускаем 29 | if (time > dist[node]) continue; 30 | 31 | // Обработка всех соседей 32 | if (graph[node]) { 33 | for (const [neighbor, weight] of graph[node]) { 34 | const d = time + weight; 35 | if (d < dist[neighbor]) { 36 | dist[neighbor] = d; 37 | pq.push([d, neighbor]); 38 | } 39 | } 40 | } 41 | } 42 | // Вычисляем максимальное время до всех узлов 43 | // Игнорируем 0-й узел 44 | const maxTime = Math.max(...dist.slice(1)); 45 | return maxTime === Infinity ? -1 : maxTime; 46 | } 47 | 48 | 49 | export function networkDelayTimeDBG(){ 50 | const tests = [ 51 | { 52 | input: { times: [[2, 1, 1], [2, 3, 1], [3, 4, 1]], n: 4, k: 2 }, 53 | expected: 2 // Сигнал доходит до всех узлов за 2 единицы времени 54 | }, 55 | { 56 | input: { times: [[1, 2, 1]], n: 2, k: 1 }, 57 | expected: 1 // Сигнал доходит до узла 2 за 1 единицу времени 58 | }, 59 | { 60 | input: { times: [[1, 2, 1]], n: 2, k: 2 }, 61 | expected: -1 // Узел 1 не может быть достигнут из узла 2 62 | } 63 | ]; 64 | 65 | tests.forEach((test, index) => { 66 | const result = networkDelayTime(test.input.times, test.input.n, test.input.k); 67 | const success = result === test.expected; 68 | if (success) { 69 | console.log(`Test ${index} success`); 70 | } else { 71 | console.log(`Test ${index} fail`); 72 | console.log(`expected: ${test.expected}`); 73 | console.log(`got: ${result}`); 74 | } 75 | }); 76 | } -------------------------------------------------------------------------------- /src/graph/num-islands.ts: -------------------------------------------------------------------------------- 1 | function numIslands(grid: string[][]): number { 2 | if (grid.length === 0) return 0; 3 | 4 | const rows = grid.length; 5 | const cols = grid[0].length; 6 | let islandCount = 0; 7 | 8 | const dfs = (r: number, c: number): void => { 9 | if (r < 0 || r >= rows || c < 0 || c >= cols || grid[r][c] === '0') { 10 | return; 11 | } 12 | 13 | grid[r][c] = '0'; 14 | 15 | dfs(r - 1, c); 16 | dfs(r + 1, c); 17 | dfs(r, c - 1); 18 | dfs(r, c + 1); 19 | }; 20 | 21 | for (let r = 0; r < rows; r++) { 22 | for (let c = 0; c < cols; c++) { 23 | if (grid[r][c] === '1') { 24 | islandCount++; 25 | dfs(r, c); 26 | } 27 | } 28 | } 29 | return islandCount; 30 | } 31 | 32 | function numIslandsDBG(){ 33 | const tests = [ 34 | { 35 | input: [ 36 | ['1', '1', '1', '1', '0'], 37 | ['1', '1', '0', '1', '0'], 38 | ['1', '1', '0', '0', '0'], 39 | ['0', '0', '0', '0', '0'] 40 | ], 41 | result: 1 42 | }, 43 | { 44 | input: [ 45 | ['1', '1', '0', '0', '0'], 46 | ['1', '1', '0', '0', '0'], 47 | ['0', '0', '1', '0', '0'], 48 | ['0', '0', '0', '1', '1'] 49 | ], 50 | result: 3 51 | } 52 | ]; 53 | 54 | tests.forEach((test, index) => { 55 | const result = numIslands(test.input); 56 | if (result === test.result) { 57 | console.log(`${index} success`); 58 | } else { 59 | console.log(`${index} fail`); 60 | console.log(`expected ${test.result}`); 61 | console.log(`got ${result}`); 62 | } 63 | }); 64 | } -------------------------------------------------------------------------------- /src/graph/utilities/graph.structure.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Пример использования: 3 | * @default 4 | * const graph: Graph = { 5 | * A: ['B', 'C'], 6 | * B: ['A', 'D'], 7 | * C: ['A', 'D'], 8 | * D: ['B', 'C', 'E'], 9 | * E: ['D'], 10 | * }; 11 | */ 12 | export type Graph = { 13 | [key: string]: string[] 14 | } -------------------------------------------------------------------------------- /src/graph/utilities/graph.utilities.ts: -------------------------------------------------------------------------------- 1 | import { Graph } from "./graph.structure"; 2 | 3 | function graphToAdjacencyMatrix(graph: Graph): number[][] { 4 | const nodes = Object.keys(graph); 5 | const n = nodes.length; 6 | const matrix = Array.from({ length: n }, () => Array(n).fill(0)); 7 | const nodeIndex: { [key: string]: number } = {}; 8 | nodes.forEach((node, index) => { 9 | nodeIndex[node] = index; 10 | }); 11 | nodes.forEach((node, i) => { 12 | graph[node].forEach(neighbor => { 13 | const j = nodeIndex[neighbor]; 14 | matrix[i][j] = 1; 15 | }); 16 | }); 17 | return matrix; 18 | } 19 | 20 | function printFormattedMatrix(matrix: number[][], nodes: string[]): void { 21 | const cellWidth = 3; 22 | const formatCell = (value: string | number) => value.toString().padStart(cellWidth); 23 | const header = " ".repeat(cellWidth) + nodes.map(node => formatCell(node)).join(""); 24 | console.log(header); 25 | console.log(" ".repeat(cellWidth) + "-".repeat(nodes.length * cellWidth)); 26 | for (let i = 0; i < matrix.length; i++) { 27 | const row = nodes[i] + " |" + matrix[i].map(value => formatCell(value)).join(""); 28 | console.log(row); 29 | } 30 | } 31 | 32 | export function printGraph(graph: Graph): void { 33 | const adjacencyMatrix = graphToAdjacencyMatrix(graph); 34 | printFormattedMatrix(adjacencyMatrix, Object.keys(graph)); 35 | } -------------------------------------------------------------------------------- /src/graph/utilities/index.ts: -------------------------------------------------------------------------------- 1 | export * from './graph.structure'; 2 | export * from './graph.utilities'; -------------------------------------------------------------------------------- /src/heap/find-relative-ranks.ts: -------------------------------------------------------------------------------- 1 | import { Heap } from "./utilities/heap"; 2 | 3 | export function findRelativeRanks(score: number[]): string[] { 4 | const result = new Array(score.length).fill(null); 5 | const placements = { 6 | 1: "Gold Medal", 7 | 2: "Silver Medal", 8 | 3: "Bronze Medal", 9 | } 10 | const pq = new Heap<[number, number]>((a, b) => { 11 | return a[1] > b[1] 12 | }); 13 | for (const [index, value] of score.entries()) { 14 | pq.push([index, value]); 15 | } 16 | for (let i = 0; i < score.length; i++) { 17 | const offset = i + 1; 18 | const [index, value] = pq.pop() 19 | result[index] = placements[offset] || String(offset); 20 | } 21 | 22 | return result; 23 | }; 24 | -------------------------------------------------------------------------------- /src/heap/max-sliding-window.ts: -------------------------------------------------------------------------------- 1 | import { Heap } from "./utilities/heap"; 2 | 3 | /** 4 | * @problem 5 | * [239. Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum) 6 | * ### Решение с использованием кучи: 7 | * Эта задача решается с использованием кучи, потому что она позволяет эффективно 8 | * поддерживать и извлекать максимум для текущего окна, обеспечивая оптимальную 9 | * сложность O(n log k) вместо медленного перебора. 10 | * 11 | * Ключевой момент, чтобы выкинуть элементы, у которых индекс вышел 12 | * за пределы окна 13 | * ``` 14 | * while (heap.top()[1] < i - k + 1) { 15 | * heap.pop(); 16 | * } 17 | * ``` 18 | */ 19 | export function maxSlidingWindow(nums: number[], k: number): number[] { 20 | const heap = new Heap<[number, number]>((a, b) => a[0] > b[0]); 21 | const result: number[] = []; 22 | 23 | for (let i = 0; i < k; i++) { 24 | heap.push([nums[i], i]); 25 | } 26 | for (let i = k - 1; i < nums.length; i++) { 27 | heap.push([nums[i], i]); 28 | while (heap.top()[1] < i - k + 1) { 29 | heap.pop(); 30 | } 31 | result.push(heap.top()[0]); 32 | } 33 | return result; 34 | } -------------------------------------------------------------------------------- /src/heap/utilities/heap.ts: -------------------------------------------------------------------------------- 1 | export class Heap { 2 | protected cmpr: (a: T, b: T) => boolean; 3 | private data: T[] = []; 4 | 5 | constructor(comparator?: (a: T, b: T) => boolean) { 6 | this.cmpr = comparator ?? ((a: T, b: T) => a > b); 7 | } 8 | 9 | private swap = (i: number, j: number): void => { 10 | [this.data[i], this.data[j]] = [this.data[j], this.data[i]]; 11 | } 12 | 13 | private parent = (index: number): number => { 14 | return Math.floor((index - 1) / 2); 15 | } 16 | 17 | private left = (index: number): number => { 18 | return 2 * index + 1; 19 | } 20 | 21 | private right = (index: number): number => { 22 | return 2 * index + 2; 23 | } 24 | 25 | private heapifyUp(index: number): void { 26 | let curr = index; 27 | 28 | const cmpr = this.cmpr; 29 | const parent = this.parent; 30 | const swap = this.swap; 31 | 32 | const data = this.data; 33 | 34 | while (curr > 0 && cmpr(data[curr], data[parent(curr)])) { 35 | const parentIx = parent(curr); 36 | swap(curr, parentIx); 37 | curr = parentIx; 38 | } 39 | } 40 | 41 | private heapifyDown(index: number): void { 42 | let curr = index; 43 | 44 | const data = this.data; 45 | 46 | const swap = this.swap; 47 | const left = this.left; 48 | const right = this.right; 49 | const cmpr = this.cmpr; 50 | 51 | while (left(curr) < data.length) { 52 | let child = left(curr); 53 | 54 | if ( 55 | right(curr) < data.length && 56 | cmpr(data[right(curr)], data[child]) 57 | ) { 58 | child = right(curr); 59 | } 60 | if (cmpr(data[curr], data[child])) { 61 | break; 62 | } 63 | swap(curr, child); 64 | curr = child; 65 | } 66 | } 67 | 68 | public pushMany(arr: T[]): void { 69 | for (const elem of arr) { 70 | this.push(elem); 71 | } 72 | } 73 | 74 | public push(value: T): void { 75 | this.data.push(value); 76 | this.heapifyUp(this.data.length - 1); 77 | } 78 | 79 | public pop(): T | null { 80 | if (this.data.length === 0) { 81 | return null; 82 | } 83 | if (this.data.length === 1) { 84 | return this.data.pop(); 85 | } 86 | const root = this.data[0]; 87 | const last = this.data.pop(); 88 | 89 | if (last !== undefined) { 90 | this.data[0] = last; 91 | this.heapifyDown(0); 92 | } 93 | return root; 94 | } 95 | 96 | public top(): T | undefined { 97 | return this.data.length === 0 ? undefined : this.data[0]; 98 | } 99 | 100 | public size(): number { 101 | return this.data.length; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/js_core/emiter-to-iterator.ts: -------------------------------------------------------------------------------- 1 | import { on, EventEmitter } from 'events'; 2 | import { get } from 'http'; 3 | 4 | export async function* fun(ee: EventEmitter) { 5 | console.log('Генератор запущен'); 6 | 7 | const res = get('a', (response) => { 8 | 9 | }) 10 | 11 | 12 | for await (const eventArgs of on(res, 'data', { close: ['end'] })) { 13 | yield eventArgs[0]; 14 | } 15 | console.log('Итератор on завершён'); 16 | } 17 | 18 | const ee = new EventEmitter(); 19 | 20 | async function test(){ 21 | const generator = fun(ee); 22 | 23 | for await (const val of generator) { 24 | console.log('Получено:', val); 25 | } 26 | 27 | console.log('Генератор завершён'); 28 | }; 29 | 30 | // Эмитируем события 31 | setTimeout(() => ee.emit('data', 'First'), 1000); 32 | setTimeout(() => ee.emit('data', 'Second'), 2000); 33 | setTimeout(() => ee.emit('data', 'Third'), 3000); 34 | setTimeout(() => ee.emit('data', 'Fourth'), 4000); 35 | setTimeout(() => ee.emit('end', 'ADSSAD'), 5000); -------------------------------------------------------------------------------- /src/js_core/flatten.ts: -------------------------------------------------------------------------------- 1 | function flat(arr: any[], depth: number): any[] { 2 | const result: any[] = []; 3 | 4 | const flattenHelper = (subArray: any[], currentDepth: number) => { 5 | for (const el of subArray) { 6 | if (Array.isArray(el) && currentDepth < depth) { 7 | flattenHelper(el, currentDepth + 1); 8 | } else { 9 | result.push(el); 10 | } 11 | } 12 | } 13 | flattenHelper(arr, 0); 14 | return result; 15 | } 16 | 17 | export function flatDBG() { 18 | const tests = [ 19 | { 20 | input: { arr: [1, [2, [3, [4]], 5]], n: 1 }, 21 | result: [1, 2, [3, [4]], 5] // Сплющено до глубины 1 22 | }, 23 | { 24 | input: { arr: [1, [2, [3, [4]], 5]], n: 2 }, 25 | result: [1, 2, 3, [4], 5] // Сплющено до глубины 2 26 | }, 27 | { 28 | input: { arr: [1, [2, [3, [4]], 5]], n: 3 }, 29 | result: [1, 2, 3, 4, 5] // Сплющено до глубины 3 30 | } 31 | ]; 32 | 33 | tests.forEach((test, index) => { 34 | const result = flat(test.input.arr, test.input.n); 35 | const success = JSON.stringify(result) === JSON.stringify(test.result); 36 | if (success) { 37 | console.log(`${index} success`); 38 | } else { 39 | console.log(`${index} fail`); 40 | console.log(`expected ${test.result}`); 41 | console.log(`got ${result}`); 42 | } 43 | }); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/js_core/promise-pool.ts: -------------------------------------------------------------------------------- 1 | async function promisePool(functions: (() => Promise)[], n: number): Promise { 2 | const pool = new Array(n).fill(null).map(() => Promise.resolve()); 3 | 4 | const executeNext = async (index: number): Promise => { 5 | if (index >= functions.length) return; 6 | await functions[index]().finally(() => executeNext(index + n)); 7 | } 8 | 9 | await Promise.all(pool.map((_, i) => executeNext(i))); 10 | } 11 | -------------------------------------------------------------------------------- /src/linked_list/delete-duplicates.ts: -------------------------------------------------------------------------------- 1 | import { ListNode } from "./linked_list.structure"; 2 | 3 | function deleteDuplicates(head: ListNode): ListNode { 4 | if (!head) return null; 5 | 6 | let dummy = new ListNode(0, head); 7 | let prev = dummy; 8 | 9 | while (head !== null) { 10 | if (head.next !== null && head.val === head.next.val) { 11 | while (head.next !== null && head.val === head.next.val) { 12 | head = head.next; 13 | } 14 | prev.next = head.next; 15 | } else { 16 | prev = prev.next; 17 | } 18 | head = head.next; 19 | } 20 | return dummy.next; 21 | }; -------------------------------------------------------------------------------- /src/linked_list/delete-middle.ts: -------------------------------------------------------------------------------- 1 | import { ListNode } from "./linked_list.structure"; 2 | import { createLinkedList, linkedListToArray } from "./utilities"; 3 | 4 | /** 5 | * @problem 6 | * [2095. Delete the Middle Node of a Linked List](https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list) 7 | */ 8 | function deleteMiddle(head: ListNode | null): ListNode | null { 9 | var slow = head; 10 | var fast = head; 11 | var prev = null; 12 | 13 | if (!head || !head.next) { 14 | return null; 15 | } 16 | while (fast && fast.next) { 17 | fast = fast.next.next; 18 | prev = slow; 19 | slow = slow.next; 20 | } 21 | 22 | prev.next = slow.next; 23 | 24 | return head; 25 | }; 26 | 27 | export function deleteMiddleDBG() { 28 | const tests = [ 29 | { 30 | input: createLinkedList([1, 3, 4, 7, 1, 2, 6]), 31 | result: [1, 3, 4, 1, 2, 6] 32 | }, 33 | { 34 | input: createLinkedList([1, 2, 3, 4]), 35 | result: [1, 2, 4] 36 | }, 37 | { 38 | input: createLinkedList([2, 1]), 39 | result: [2] 40 | }, 41 | { 42 | input: createLinkedList([1]), 43 | result: [] 44 | }, 45 | { 46 | input: createLinkedList([]), 47 | result: [] 48 | } 49 | ]; 50 | 51 | tests.forEach((test, index) => { 52 | const output = deleteMiddle(test.input); 53 | const outputArray = linkedListToArray(output); 54 | const success = JSON.stringify(outputArray) === JSON.stringify(test.result); 55 | if (success) { 56 | console.log(`${index} success`); 57 | } else { 58 | console.log(`${index} fail`); 59 | console.log(`expected ${JSON.stringify(test.result)}`); 60 | console.log(`got ${JSON.stringify(outputArray)}`); 61 | } 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /src/linked_list/linked_list.structure.ts: -------------------------------------------------------------------------------- 1 | export class ListNode { 2 | val: number; 3 | next: ListNode | null; 4 | constructor(val?: number, next?: ListNode | null) { 5 | this.val = (val === undefined ? 0 : val); 6 | this.next = (next === undefined ? null : next); 7 | } 8 | } -------------------------------------------------------------------------------- /src/linked_list/merge-k-lists.ts: -------------------------------------------------------------------------------- 1 | import { ListNode } from "./linked_list.structure"; 2 | 3 | 4 | function mergeKLists(lists: Array): ListNode | null { 5 | const mergeTwoLists = (l1: ListNode | null, l2: ListNode | null): ListNode | null => { 6 | const dummy = new ListNode(); 7 | let current = dummy; 8 | 9 | while (l1 !== null && l2 !== null) { 10 | if (l1.val < l2.val) { 11 | current.next = l1; 12 | l1 = l1.next; 13 | } else { 14 | current.next = l2; 15 | l2 = l2.next; 16 | } 17 | current = current.next; 18 | } 19 | 20 | if (l1 !== null) current.next = l1; 21 | if (l2 !== null) current.next = l2; 22 | 23 | return dummy.next; 24 | }; 25 | 26 | if (lists.length === 0) return null; 27 | 28 | while (lists.length > 1) { 29 | let mergedLists: Array = []; 30 | 31 | for (let i = 0; i < lists.length; i += 2) { 32 | const l1 = lists[i]; 33 | const l2 = i + 1 < lists.length ? lists[i + 1] : null; 34 | mergedLists.push(mergeTwoLists(l1, l2)); 35 | } 36 | 37 | lists = mergedLists; 38 | } 39 | 40 | return lists[0]; 41 | } -------------------------------------------------------------------------------- /src/linked_list/merge-two-lists.ts: -------------------------------------------------------------------------------- 1 | import { ListNode } from "./linked_list.structure"; 2 | import { createLinkedList, linkedListToArray } from "./utilities"; 3 | 4 | function mergeTwoLists(l1: ListNode | null, l2: ListNode | null): ListNode | null { 5 | const dummy = new ListNode(0); 6 | let current = dummy; 7 | 8 | while (l1 && l2) { 9 | if (l1.val < l2.val) { 10 | current.next = l1; 11 | l1 = l1.next; 12 | } else { 13 | current.next = l2; 14 | l2 = l2.next; 15 | } 16 | current = current.next; 17 | } 18 | 19 | // Если остались элементы в одном из списков, присоединяем их 20 | current.next = l1 ? l1 : l2; 21 | 22 | return dummy.next; 23 | } 24 | 25 | 26 | function mergeTwoListsDBG() { 27 | const tests = [ 28 | { 29 | input: { 30 | l1: createLinkedList([1, 2, 4]), 31 | l2: createLinkedList([1, 3, 4]) 32 | }, 33 | result: [1, 1, 2, 3, 4, 4] 34 | }, 35 | { 36 | input: { 37 | l1: createLinkedList([]), 38 | l2: createLinkedList([]) 39 | }, 40 | result: [] 41 | }, 42 | { 43 | input: { 44 | l1: createLinkedList([]), 45 | l2: createLinkedList([0]) 46 | }, 47 | result: [0] 48 | } 49 | ]; 50 | 51 | tests.forEach((test, index) => { 52 | const mergedList = mergeTwoLists(test.input.l1, test.input.l2); 53 | const mergedArray = linkedListToArray(mergedList); 54 | const success = JSON.stringify(mergedArray) === JSON.stringify(test.result); 55 | if (success) { 56 | console.log(`${index} success`); 57 | } else { 58 | console.log(`${index} fail`); 59 | console.log(`expected ${JSON.stringify(test.result)}`); 60 | console.log(`got ${JSON.stringify(mergedArray)}`); 61 | } 62 | }); 63 | } -------------------------------------------------------------------------------- /src/linked_list/odd-even-list.ts: -------------------------------------------------------------------------------- 1 | import { ListNode } from "./linked_list.structure"; 2 | import { createLinkedList, linkedListToArray } from "./utilities"; 3 | 4 | /** 5 | * @problem 6 | * [328. Odd Even Linked List](https://leetcode.com/problems/odd-even-linked-list) 7 | */ 8 | function oddEvenList(head: ListNode | null): ListNode | null { 9 | if (!head) { 10 | return null; 11 | } 12 | if (!head.next) { 13 | return head; 14 | } 15 | let odd = head; 16 | let even = head.next; 17 | let evenHead = even; 18 | 19 | while (even && even.next) { 20 | odd.next = even.next; 21 | odd = odd.next; 22 | even.next = odd.next; 23 | even = even.next; 24 | } 25 | odd.next = evenHead; 26 | return head; 27 | }; 28 | 29 | export function oddEvenListDBG() { 30 | const tests = [ 31 | { 32 | input: createLinkedList([1, 2, 3, 4, 5]), 33 | result: [1, 3, 5, 2, 4] 34 | }, 35 | { 36 | input: createLinkedList([2, 1, 3, 5, 6, 4, 7]), 37 | result: [2, 3, 6, 7, 1, 5, 4] 38 | }, 39 | { 40 | input: createLinkedList([]), 41 | result: [] 42 | }, 43 | { 44 | input: createLinkedList([1]), 45 | result: [1] 46 | }, 47 | { 48 | input: createLinkedList([1, 2]), 49 | result: [1, 2] 50 | } 51 | ]; 52 | 53 | tests.forEach((test, index) => { 54 | const oddEven = oddEvenList(test.input); 55 | const resultArray = linkedListToArray(oddEven); 56 | const success = JSON.stringify(resultArray) === JSON.stringify(test.result); 57 | if (success) { 58 | console.log(`${index} success`); 59 | } else { 60 | console.log(`${index} fail`); 61 | console.log(`expected ${JSON.stringify(test.result)}`); 62 | console.log(`got ${JSON.stringify(resultArray)}`); 63 | } 64 | }); 65 | } -------------------------------------------------------------------------------- /src/linked_list/remove-nth-fromend.ts: -------------------------------------------------------------------------------- 1 | import { ListNode } from "./linked_list.structure"; 2 | 3 | function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null { 4 | const dummy = new ListNode(0, head); 5 | let first: ListNode | null = dummy; 6 | let second: ListNode | null = dummy; 7 | 8 | // Сдвигаем первый указатель на n+1 шагов вперед 9 | for (let i = 0; i <= n; i++) { 10 | if (first === null) return head; // Если n больше длины списка 11 | first = first.next; 12 | } 13 | 14 | // Перемещаем оба указателя до конца списка 15 | while (first !== null) { 16 | first = first.next; 17 | second = second!.next; 18 | } 19 | 20 | // Удаляем n-ый узел с конца 21 | second!.next = second!.next!.next; 22 | 23 | return dummy.next; 24 | } 25 | -------------------------------------------------------------------------------- /src/linked_list/reverse-list.ts: -------------------------------------------------------------------------------- 1 | import { ListNode } from "./linked_list.structure"; 2 | 3 | function reverseList(head: ListNode | null): ListNode | null { 4 | let prev: ListNode | null = null; 5 | let curr: ListNode | null = head; 6 | 7 | while (curr !== null) { 8 | const next: ListNode | null = curr.next; 9 | curr.next = prev; 10 | prev = curr; 11 | curr = next; 12 | } 13 | return prev; 14 | } -------------------------------------------------------------------------------- /src/linked_list/split-list-to-parts.ts: -------------------------------------------------------------------------------- 1 | import { ListNode } from "./linked_list.structure"; 2 | 3 | 4 | /* 5 | 6 | Input: head = [1,2,3,4,5,6,7,8,9,10], k = 3 7 | Output: [[1,2,3,4],[5,6,7],[8,9,10]] 8 | 9 | */ 10 | function splitListToParts(head: ListNode, k: number) { 11 | 12 | const result = new Array(k).fill(null); 13 | let length = 0; 14 | let current = head; 15 | 16 | while (current !== null) { 17 | length++; 18 | current = current.next; 19 | } 20 | 21 | let carry = length % k; 22 | const baseSize = Math.floor(length / k); 23 | 24 | current = head; 25 | let prev: ListNode | null = null; 26 | 27 | for (let i = 0; i < k; i++) { 28 | if (!current) { 29 | result[i] = null; 30 | } else { 31 | result[i] = current; 32 | let partSize = baseSize + (carry > 0 ? 1 : 0); 33 | carry--; 34 | 35 | for (let j = 0; j < partSize; j++) { 36 | prev = current; 37 | current = current!.next; 38 | } 39 | if (prev !== null) { 40 | prev.next = null; 41 | } 42 | } 43 | } 44 | return result; 45 | } 46 | 47 | 48 | export function splitListToPartsDBG() { 49 | class ListNode { 50 | val: number; 51 | next: ListNode | null; 52 | constructor(val?: number, next?: ListNode | null) { 53 | this.val = (val === undefined ? 0 : val); 54 | this.next = (next === undefined ? null : next); 55 | } 56 | } 57 | 58 | const tests = [ 59 | { 60 | input: { 61 | root: new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4)))), 62 | k: 5 63 | }, 64 | expected: [ 65 | new ListNode(1), 66 | new ListNode(2), 67 | new ListNode(3), 68 | new ListNode(4), 69 | null 70 | ] 71 | }, 72 | { 73 | input: { 74 | root: new ListNode(1, new ListNode(2, new ListNode(3))), 75 | k: 2 76 | }, 77 | expected: [ 78 | new ListNode(1, new ListNode(2)), 79 | new ListNode(3) 80 | ] 81 | }, 82 | ]; 83 | 84 | tests.forEach((test, index) => { 85 | const result = splitListToParts(test.input.root, test.input.k); 86 | const success = JSON.stringify(result) === JSON.stringify(test.expected); 87 | if (success) { 88 | console.log(`Test ${index} success`); 89 | } else { 90 | console.log(`Test ${index} fail`); 91 | console.log(`expected: ${JSON.stringify(test.expected)}`); 92 | console.log(`got: ${JSON.stringify(result)}`); 93 | } 94 | }); 95 | 96 | } -------------------------------------------------------------------------------- /src/linked_list/utilities.ts: -------------------------------------------------------------------------------- 1 | import { ListNode } from "./linked_list.structure"; 2 | 3 | export function createLinkedList(arr: number[]): ListNode | null { 4 | if (arr.length === 0) return null; 5 | let head = new ListNode(arr[0]); 6 | let current = head; 7 | for (let i = 1; i < arr.length; i++) { 8 | current.next = new ListNode(arr[i]); 9 | current = current.next; 10 | } 11 | return head; 12 | } 13 | 14 | // Функция для преобразования связанного списка в массив 15 | export function linkedListToArray(head: ListNode | null): number[] { 16 | const result: number[] = []; 17 | let current = head; 18 | while (current) { 19 | result.push(current.val); 20 | current = current.next; 21 | } 22 | return result; 23 | } -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { leafSimilarDBG } from "./bst/leaf-similar"; 2 | 3 | leafSimilarDBG() -------------------------------------------------------------------------------- /src/multi-thread/base-pool.ts: -------------------------------------------------------------------------------- 1 | import { Piscina } from "piscina"; 2 | import { join } from 'path' 3 | 4 | const piscina = new Piscina({ 5 | filename: join(__dirname, 'worker/worker.js'), 6 | maxThreads: 12 7 | }); 8 | 9 | async function main() { 10 | const now = Date.now(); 11 | 12 | const tasks = Array.from({ length: 24 }, (_, idx) => idx).map((id) => 13 | piscina.run({ taskId: id, duration: 3000 }) 14 | ); 15 | 16 | const results = await Promise.all(tasks); 17 | const totalTime = Date.now() - now; 18 | 19 | results.forEach((res) => console.log(res)); 20 | console.log(`Total time elapsed: ${totalTime} ms`); 21 | } -------------------------------------------------------------------------------- /src/multi-thread/worker/worker.ts: -------------------------------------------------------------------------------- 1 | import { parentPort, threadId } from 'worker_threads'; 2 | 3 | export default ({ taskId, duration }: { taskId: number; duration: number }) => { 4 | const start = Date.now(); 5 | const end = start + duration; 6 | 7 | // "Тяжёлая" задача — имитация загрузки CPU 8 | while (Date.now() < end) { 9 | // Пустой цикл для загрузки процессора 10 | } 11 | 12 | const actualDuration = Date.now() - start; 13 | return `Task ${taskId} completed after ${actualDuration} ms (on thread: ${threadId})`; 14 | }; -------------------------------------------------------------------------------- /src/queue/predict-party-victory.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @problem 3 | * [649. Dota2 Senate](https://leetcode.com/problems/dota2-senate) 4 | */ 5 | function predictPartyVictory(senate: string): string { 6 | const n = senate.length; 7 | 8 | const rQueue: number[] = []; 9 | const dQueue: number[] = []; 10 | 11 | for (let i = 0; i < n; i++) { 12 | if (senate[i] === 'R') { 13 | rQueue.push(i); 14 | } else { 15 | dQueue.push(i); 16 | } 17 | } 18 | 19 | while (rQueue.length > 0 && dQueue.length > 0) { 20 | const rTurn = rQueue.shift(); 21 | const dTurn = dQueue.shift(); 22 | 23 | if (dTurn < rTurn) { 24 | dQueue.push(dTurn + n); 25 | } else { 26 | rQueue.push(rTurn + n); 27 | } 28 | } 29 | return rQueue.length === 0 30 | ? "Dire" 31 | : "Radiant" 32 | }; 33 | 34 | 35 | 36 | export function dota2SenateDBG() { 37 | const tests = [ 38 | { 39 | input: 'DDRRR', 40 | result: 'Dire' 41 | }, 42 | { 43 | input: 'DRRDRDRDRDDRDRDRRDR', 44 | result: 'Radiant' 45 | }, 46 | { 47 | input: 'RRDDD', 48 | result: 'Radiant' 49 | }, 50 | { 51 | input: 'RRRRRDDDD', 52 | result: 'Radiant' 53 | }, 54 | ]; 55 | 56 | tests.forEach((test, index) => { 57 | const result = predictPartyVictory(test.input); 58 | const success = result === test.result; 59 | if (success) { 60 | console.log(`Test ${index} success`); 61 | } else { 62 | console.log(`Test ${index} fail`); 63 | console.log(`expected "${test.result}"`); 64 | console.log(`got "${result}"`); 65 | } 66 | }); 67 | } -------------------------------------------------------------------------------- /src/recursion/generate-parenthesis.ts: -------------------------------------------------------------------------------- 1 | function generateParenthesis(n: number): string[] { 2 | const result: string[] = []; 3 | 4 | const backtrack = (current: string, open: number, close: number) => { 5 | if (current.length === n * 2) { 6 | result.push(current); 7 | return; 8 | } 9 | if (open < n) { 10 | backtrack(current + '(', open + 1, close); 11 | } 12 | if (close < open) { 13 | backtrack(current + ')', open, close + 1); 14 | } 15 | }; 16 | 17 | backtrack('', 0, 0); 18 | return result; 19 | } 20 | 21 | export function generateParenthesisDBG(){ 22 | const tests = [ 23 | { 24 | input: 3, 25 | result: ["((()))", "(()())", "(())()", "()(())", "()()()"] 26 | }, 27 | { 28 | input: 1, 29 | result: ["()"] 30 | } 31 | ]; 32 | 33 | tests.forEach((test, index) => { 34 | const result = generateParenthesis(test.input); 35 | const success = JSON.stringify(result.sort()) === JSON.stringify(test.result.sort()); 36 | if (success) { 37 | console.log(`${index} success`); 38 | } else { 39 | console.log(`${index} fail`); 40 | console.log(`expected ${test.result}`); 41 | console.log(`got ${result}`); 42 | } 43 | }); 44 | } -------------------------------------------------------------------------------- /src/recursion/problems.ts: -------------------------------------------------------------------------------- 1 | export function checkMaxRecursionDepth(depth = 0) { 2 | try { 3 | return checkMaxRecursionDepth(depth + 1); 4 | } catch (e) { 5 | return depth; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/scratch_structure/atm.ts: -------------------------------------------------------------------------------- 1 | class ATM { 2 | private banknotes: number[]; 3 | private readonly denominations: number[] = [20, 50, 100, 200, 500]; 4 | 5 | constructor() { 6 | this.banknotes = new Array(5).fill(0); 7 | } 8 | 9 | deposit(banknotesCount: number[]): void { 10 | for (let i = 0; i < 5; i++) { 11 | this.banknotes[i] += banknotesCount[i]; 12 | } 13 | } 14 | 15 | withdraw(amount: number): number[] { 16 | const usedBanknotes = new Array(5).fill(0); 17 | let remainingAmount = amount; 18 | 19 | for (let i = 4; i >= 0; i--) { 20 | const banknoteValue = this.denominations[i]; 21 | const maxBanknotes = Math.min( 22 | Math.floor(remainingAmount / banknoteValue), this.banknotes[i] 23 | ); 24 | usedBanknotes[i] = maxBanknotes; 25 | remainingAmount -= maxBanknotes * banknoteValue; 26 | } 27 | 28 | if (remainingAmount > 0) { 29 | return [-1]; 30 | } 31 | 32 | for (let i = 0; i < 5; i++) { 33 | this.banknotes[i] -= usedBanknotes[i]; 34 | } 35 | 36 | return usedBanknotes; 37 | } 38 | } 39 | 40 | 41 | export function AtmTEST() { 42 | const atm = new ATM(); 43 | atm.deposit([0, 0, 1, 2, 1]); // Добавляем 0 банкнот по 20, 0 банкнот по 50, 1 банкноту по 100, 2 банкноты по 200, и 1 банкноту по 500 44 | console.log(atm.withdraw(600)); // Ожидаемый результат: [0, 0, 1, 0, 1] (1 банкнота по 100, 1 банкнота по 500) 45 | atm.deposit([0, 1, 0, 1, 1]); 46 | console.log(atm.withdraw(600)); // Ожидаемый результат: [0, 0, 1, 2, 0] (1 банкнота по 100, 2 банкноты по 200) 47 | console.log(atm.withdraw(550)); // Ожидаемый результат: [-1] (невозможно выдать сумму) 48 | } -------------------------------------------------------------------------------- /src/scratch_structure/event-emitter.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @default 4 | * const emitter = new EventEmitterScratch(); 5 | * 6 | * const sub1 = emitter.subscribe('event1', (x: number) => x + 1); 7 | * const sub2 = emitter.subscribe('event2', (x: number) => x * 2); 8 | * 9 | * sub1.unsubscribe() 10 | */ 11 | export class EventEmitterScratch { 12 | private events: Map; 13 | 14 | constructor() { 15 | this.events = new Map(); 16 | } 17 | 18 | subscribe(eventName: string, callback: Function) { 19 | if (!this.events.has(eventName)) { 20 | this.events.set(eventName, []); 21 | } 22 | 23 | this.events.get(eventName)!.push(callback); 24 | 25 | return { 26 | unsubscribe: function() { 27 | const callbacks = this.events.get(eventName); 28 | if (callbacks) { 29 | const index = callbacks.indexOf(callback); 30 | if (index !== -1) { 31 | callbacks.splice(index, 1); 32 | } 33 | } 34 | } 35 | }; 36 | } 37 | 38 | emit(eventName: string, args: any[] = []): any[] { 39 | const callbacks = this.events.get(eventName); 40 | if (!callbacks) { 41 | return []; 42 | } 43 | 44 | return callbacks.map(callback => callback(...args)); 45 | } 46 | } -------------------------------------------------------------------------------- /src/scratch_structure/hit-counter.ts: -------------------------------------------------------------------------------- 1 | class HitCounter { 2 | private hits: [number, number][]; 3 | 4 | constructor() { 5 | this.hits = []; 6 | } 7 | 8 | hit(timestamp: number): void { 9 | if (this.hits.length > 0 && this.hits[this.hits.length - 1][0] === timestamp) { 10 | this.hits[this.hits.length - 1][1]++; 11 | } else { 12 | this.hits.push([timestamp, 1]); 13 | } 14 | } 15 | 16 | getHits(timestamp: number): number { 17 | while (this.hits.length > 0 && this.hits[0][0] <= timestamp - 300) { 18 | this.hits.shift(); 19 | } 20 | return this.hits.reduce((acc, hit) => acc += hit[1], 0); 21 | } 22 | } 23 | 24 | export function HitCounterDBG() { 25 | const hitCounter = new HitCounter(); 26 | hitCounter.hit(1); 27 | hitCounter.hit(2); 28 | hitCounter.hit(3); 29 | // Ожидаемый результат: 3 30 | console.log(hitCounter.getHits(4)); 31 | hitCounter.hit(4); 32 | // Ожидаемый результат: 4 33 | console.log(hitCounter.getHits(300)); 34 | // Ожидаемый результат: 3 35 | console.log(hitCounter.getHits(301)); 36 | } 37 | -------------------------------------------------------------------------------- /src/scratch_structure/lru-cache.ts: -------------------------------------------------------------------------------- 1 | class LRUCache { 2 | private cache: Map; 3 | private capacity: number; 4 | 5 | constructor(capacity: number) { 6 | this.cache = new Map(); 7 | this.capacity = capacity; 8 | } 9 | 10 | get(key: number): number { 11 | if (!this.cache.has(key)) return -1; 12 | 13 | const value = this.cache.get(key)!; 14 | this.cache.delete(key); 15 | this.cache.set(key, value); 16 | 17 | return value; 18 | } 19 | 20 | put(key: number, value: number): void { 21 | if (this.cache.has(key)) { 22 | this.cache.delete(key); 23 | } else if (this.cache.size >= this.capacity) { 24 | const leastUsedKey = this.cache.keys().next().value; 25 | this.cache.delete(leastUsedKey); 26 | } 27 | 28 | this.cache.set(key, value); 29 | } 30 | } 31 | 32 | 33 | export function testLRU(){ 34 | const tests = [ 35 | { 36 | input: [ 37 | ['LRUCache', 'put', 'put', 'get', 'put', 'get', 'put', 'get', 'get', 'get'], 38 | [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] 39 | ], 40 | result: [null, null, 1, null, -1, null, -1, 3, 4] 41 | } 42 | ]; 43 | 44 | tests.forEach((test, index) => { 45 | const [commands, values] = test.input; 46 | const cache = new LRUCache(values[0][0] as number); 47 | const result = []; 48 | 49 | for (let i = 1; i < commands.length; i++) { 50 | if (commands[i] === 'get') { 51 | result.push(cache.get(values[i][0] as number)); 52 | } else if (commands[i] === 'put') { 53 | cache.put(values[i][0] as number, values[i][1] as number); 54 | result.push(null); 55 | } 56 | } 57 | 58 | const success = JSON.stringify(result) === JSON.stringify(test.result); 59 | if (success) { 60 | console.log(`${index} success`); 61 | } else { 62 | console.log(`${index} fail`); 63 | console.log(`expected ${JSON.stringify(test.result)}`); 64 | console.log(`got ${JSON.stringify(result)}`); 65 | } 66 | }); 67 | } -------------------------------------------------------------------------------- /src/scratch_structure/min-stack.ts: -------------------------------------------------------------------------------- 1 | class MinStack { 2 | private stack: number[] = []; 3 | private minStack: number[] = []; 4 | 5 | push(val: number): void { 6 | this.stack.push(val); 7 | if (this.minStack.length === 0 || val <= this.minStack[this.minStack.length - 1]) { 8 | this.minStack.push(val); 9 | } 10 | } 11 | 12 | pop(): void { 13 | const removed = this.stack.pop(); 14 | if (removed === this.minStack[this.minStack.length - 1]) { 15 | this.minStack.pop(); 16 | } 17 | } 18 | 19 | top(): number { 20 | return this.stack[this.stack.length - 1]; 21 | } 22 | 23 | getMin(): number { 24 | return this.minStack[this.minStack.length - 1]; 25 | } 26 | } 27 | 28 | 29 | export function testMinStack() { 30 | const commands = ['MinStack', 'push', 'push', 'push', 'getMin', 'pop', 'top', 'getMin']; 31 | const values = [[], [-2], [0], [-3], [], [], [], []]; 32 | const expected = [null, null, null, null, -3, null, 0, -2]; 33 | 34 | const stack = new MinStack(); 35 | const results = commands.map((command, index) => { 36 | if (command === 'push') { 37 | stack.push(values[index][0]); 38 | return null; 39 | } else if (command === 'pop') { 40 | stack.pop(); 41 | return null; 42 | } else if (command === 'top') { 43 | return stack.top(); 44 | } else if (command === 'getMin') { 45 | return stack.getMin(); 46 | } 47 | }); 48 | 49 | console.log(results); 50 | } -------------------------------------------------------------------------------- /src/scratch_structure/nested-iterator.ts: -------------------------------------------------------------------------------- 1 | class NestedInteger { 2 | private value: number | NestedInteger[] | null; 3 | 4 | constructor(value: number | NestedInteger[] | null = null) { 5 | this.value = value; 6 | } 7 | 8 | isInteger(): boolean { 9 | return typeof this.value === 'number'; 10 | } 11 | 12 | getInteger(): number | null { 13 | return this.isInteger() ? (this.value as number) : null; 14 | } 15 | 16 | getList(): NestedInteger[] { 17 | return !this.isInteger() ? (this.value as NestedInteger[]) : []; 18 | } 19 | } 20 | 21 | 22 | class NestedIterator { 23 | private stack: NestedInteger[] = []; 24 | 25 | constructor(nestedList: NestedInteger[]) { 26 | this.stack = [...nestedList.reverse()]; 27 | } 28 | 29 | hasNext(): boolean { 30 | while (this.stack.length > 0) { 31 | const top = this.stack[this.stack.length - 1]; 32 | if (top.isInteger()) { 33 | return true; 34 | } else { 35 | this.stack.pop(); 36 | this.stack.push(...top.getList().reverse()); 37 | } 38 | } 39 | return false; 40 | } 41 | 42 | next(): number { 43 | return this.stack.pop()!.getInteger()!; 44 | } 45 | } 46 | 47 | 48 | function testNestedIterator() { 49 | // Пример 1: [1, [4, [6]]] 50 | const nestedList1 = [ 51 | new NestedInteger(1), 52 | new NestedInteger([ 53 | new NestedInteger(4), 54 | new NestedInteger([ 55 | new NestedInteger(6) 56 | ]) 57 | ]) 58 | ]; 59 | 60 | const iterator1 = new NestedIterator(nestedList1); 61 | const result1: number[] = []; 62 | while (iterator1.hasNext()) { 63 | result1.push(iterator1.next()); 64 | } 65 | console.log(result1); // Ожидаемый результат: [1, 4, 6] 66 | 67 | // Пример 2: [[1, 1], 2, [1, 1]] 68 | const nestedList2 = [ 69 | new NestedInteger([ 70 | new NestedInteger(1), 71 | new NestedInteger(1) 72 | ]), 73 | new NestedInteger(2), 74 | new NestedInteger([ 75 | new NestedInteger(1), 76 | new NestedInteger(1) 77 | ]) 78 | ]; 79 | 80 | const iterator2 = new NestedIterator(nestedList2); 81 | const result2: number[] = []; 82 | while (iterator2.hasNext()) { 83 | result2.push(iterator2.next()); 84 | } 85 | console.log(result2); // Ожидаемый результат: [1, 1, 2, 1, 1] 86 | } 87 | 88 | // Запуск тестов 89 | testNestedIterator(); -------------------------------------------------------------------------------- /src/scratch_structure/randomized-set.ts: -------------------------------------------------------------------------------- 1 | class RandomizedSet { 2 | private map: Map; 3 | private values: number[]; 4 | 5 | constructor() { 6 | this.map = new Map(); 7 | this.values = []; 8 | } 9 | 10 | insert(val: number): boolean { 11 | if (this.map.has(val)) { 12 | return false; 13 | } 14 | this.map.set(val, this.values.length); 15 | this.values.push(val); 16 | return true; 17 | } 18 | 19 | remove(val: number): boolean { 20 | if (!this.map.has(val)) { 21 | return false; 22 | } 23 | const idx = this.map.get(val)!; 24 | const lastVal = this.values[this.values.length - 1]; 25 | 26 | // Меняем местами удаляемый элемент с последним 27 | this.values[idx] = lastVal; 28 | this.map.set(lastVal, idx); 29 | 30 | // Удаляем последний элемент 31 | this.values.pop(); 32 | this.map.delete(val); 33 | return true; 34 | } 35 | 36 | getRandom(): number { 37 | const randomIndex = Math.floor(Math.random() * this.values.length); 38 | return this.values[randomIndex]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/scratch_structure/rate-limiter.ts: -------------------------------------------------------------------------------- 1 | class RateLimiter { 2 | private last: number; 3 | 4 | constructor() { 5 | this.last = 0; 6 | } 7 | 8 | public async wait(interval: number): Promise { 9 | const curr = new Date().getTime(); 10 | const diff = this.last - curr; 11 | 12 | if (diff >= 0) { 13 | this.last = this.last + interval; 14 | await new Promise(res => setTimeout(res, diff)) 15 | } else { 16 | this.last = curr + interval; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/scratch_structure/recent-counter.ts: -------------------------------------------------------------------------------- 1 | class RecentCounter { 2 | private queue: number[]; 3 | 4 | constructor() { 5 | this.queue = []; 6 | } 7 | 8 | ping(t: number): number { 9 | this.queue.push(t); 10 | 11 | while (this.queue.length > 0 && this.queue[0] < t - 3000) { 12 | this.queue.shift(); 13 | } 14 | return this.queue.length; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/scratch_structure/stack-queue.ts: -------------------------------------------------------------------------------- 1 | class MyQueue { 2 | private stackIn: number[]; 3 | private stackOut: number[]; 4 | 5 | constructor() { 6 | this.stackIn = []; 7 | this.stackOut = []; 8 | } 9 | 10 | enqueue(x: number): void { 11 | this.stackIn.push(x); 12 | } 13 | 14 | dequeue(): number | null { 15 | if (this.isEmpty()) return null; 16 | this.moveInToOut(); 17 | return this.stackOut.pop() ?? null; 18 | } 19 | 20 | front(): number | null { 21 | if (this.isEmpty()) return null; 22 | this.moveInToOut(); 23 | return this.stackOut[this.stackOut.length - 1] ?? null; 24 | } 25 | 26 | isEmpty(): boolean { 27 | return this.stackIn.length === 0 && this.stackOut.length === 0; 28 | } 29 | 30 | private moveInToOut(): void { 31 | if (this.stackOut.length === 0) { 32 | while (this.stackIn.length > 0) { 33 | this.stackOut.push(this.stackIn.pop()!); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/scratch_structure/unique-structure.ts: -------------------------------------------------------------------------------- 1 | export class UniqueStructure { 2 | 3 | private elementCount: Map; 4 | private uniqueElements: Set; 5 | 6 | constructor() { 7 | this.elementCount = new Map(); 8 | this.uniqueElements = new Set(); 9 | } 10 | 11 | public add(element: E) { 12 | const count = this.elementCount.get(element) || 0; 13 | this.elementCount.set(element, count + 1); 14 | 15 | if (count === 0) { 16 | this.uniqueElements.add(element); 17 | } else { 18 | this.uniqueElements.delete(element); 19 | } 20 | } 21 | 22 | public delete(element: E) { 23 | const count = this.elementCount.get(element); 24 | 25 | if (count === 1) { 26 | this.elementCount.delete(element); 27 | this.uniqueElements.delete(element); 28 | } else { 29 | this.elementCount.set(element, count - 1); 30 | if (count === 2) { 31 | this.uniqueElements.add(element); 32 | } 33 | } 34 | } 35 | 36 | public getUnique() { 37 | const uniqueIterator = this.elementCount.keys(); 38 | const firstUnique = uniqueIterator.next().value; 39 | 40 | return firstUnique ? firstUnique : null; 41 | } 42 | } -------------------------------------------------------------------------------- /src/scratch_structure/zigzag-iterator.ts: -------------------------------------------------------------------------------- 1 | class ZigzagIterator { 2 | private queues: number[][]; 3 | private currentQueue: number; 4 | 5 | constructor(v1: number[], v2: number[]) { 6 | this.queues = []; 7 | if (v1.length > 0) this.queues.push(v1); 8 | if (v2.length > 0) this.queues.push(v2); 9 | this.currentQueue = 0; 10 | } 11 | 12 | next(): number { 13 | if (!this.hasNext()) { 14 | throw new Error("No more elements"); 15 | } 16 | 17 | const queue = this.queues[this.currentQueue]; 18 | const value = queue.shift()!; // Извлекаем первый элемент из текущей очереди 19 | 20 | // Если очередь не пуста после извлечения, перемещаемся к следующей 21 | if (queue.length === 0) { 22 | this.queues.splice(this.currentQueue, 1); 23 | if (this.currentQueue >= this.queues.length) { 24 | this.currentQueue = 0; 25 | } 26 | } else { 27 | this.currentQueue = (this.currentQueue + 1) % this.queues.length; 28 | } 29 | 30 | return value; 31 | } 32 | 33 | hasNext(): boolean { 34 | return this.queues.length > 0; 35 | } 36 | } -------------------------------------------------------------------------------- /src/sort/bubble.sort.ts: -------------------------------------------------------------------------------- 1 | import { defaultComparator } from "./utilities"; 2 | 3 | Array.prototype.bubbleSort = function( 4 | compareFn: (a: T, b: T) => number = defaultComparator 5 | ): T[] { 6 | const arr = this as T[]; 7 | const len = arr.length; 8 | 9 | for (let i = 0; i < len; i++) { 10 | for (let j = 0; j < len - 1 - i; j++) { 11 | if (compareFn(arr[j], arr[j + 1]) > 0) { 12 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; 13 | } 14 | } 15 | } 16 | return arr; 17 | } -------------------------------------------------------------------------------- /src/sort/bucket.sort.ts: -------------------------------------------------------------------------------- 1 | Array.prototype.bucketSort = function(): number[] { 2 | const arr = this as number[]; 3 | const bucketSize = 5; 4 | 5 | if (arr.length === 0) { 6 | return arr; 7 | } 8 | 9 | const min = Math.min(...arr); 10 | const max = Math.max(...arr); 11 | const bucketCount = Math.floor((max - min) / bucketSize) + 1; 12 | const buckets: number[][] = new Array(bucketCount).fill(null).map(() => []); 13 | 14 | for (let i = 0; i < arr.length; i++) { 15 | const bucketIndex = Math.floor((arr[i] - min) / bucketSize); 16 | buckets[bucketIndex].push(arr[i]); 17 | } 18 | 19 | return buckets.reduce((sortedArr, bucket) => { 20 | bucket.sort((a, b) => a - b); 21 | return sortedArr.concat(bucket); 22 | }, []); 23 | }; 24 | 25 | export {}; -------------------------------------------------------------------------------- /src/sort/cocktail.sort.ts: -------------------------------------------------------------------------------- 1 | import { defaultComparator } from "./utilities"; 2 | 3 | Array.prototype.cocktailSort = function( 4 | compareFn: (a: T, b: T) => number = defaultComparator 5 | ): T[] { 6 | const arr = this as T[]; 7 | let swapped = true; 8 | let start = 0; 9 | let end = arr.length; 10 | 11 | while (swapped) { 12 | swapped = false; 13 | for (let i = start; i < end - 1; i++) { 14 | if (compareFn(arr[i], arr[i + 1]) > 0) { 15 | [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]]; 16 | swapped = true; 17 | } 18 | } 19 | 20 | if (!swapped) break; 21 | 22 | swapped = false; 23 | end--; 24 | 25 | for (let i = end - 1; i >= start; i--) { 26 | if (compareFn(arr[i], arr[i + 1]) > 0) { 27 | [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]]; 28 | swapped = true; 29 | } 30 | } 31 | 32 | start++; 33 | } 34 | 35 | return arr; 36 | }; 37 | export {} -------------------------------------------------------------------------------- /src/sort/comb.sort.ts: -------------------------------------------------------------------------------- 1 | import { defaultComparator } from "./utilities"; 2 | 3 | Array.prototype.combSort = function( 4 | compareFn: (a: T, b: T) => number = defaultComparator 5 | ): T[] { 6 | const arr = this as T[]; 7 | const len = arr.length; 8 | let gap = len; 9 | let swapped = true; 10 | 11 | while (gap !== 1 || swapped) { 12 | gap = Math.floor(gap / 1.3); 13 | if (gap < 1) gap = 1; 14 | 15 | swapped = false; 16 | for (let i = 0; i < len - gap; i++) { 17 | if (compareFn(arr[i], arr[i + gap]) > 0) { 18 | [arr[i], arr[i + gap]] = [arr[i + gap], arr[i]]; 19 | swapped = true; 20 | } 21 | } 22 | } 23 | 24 | return arr; 25 | }; -------------------------------------------------------------------------------- /src/sort/counting.sort.ts: -------------------------------------------------------------------------------- 1 | Array.prototype.countingSort = function(): number[] { 2 | const arr = this as number[]; 3 | const max = Math.max(...arr); 4 | const min = Math.min(...arr); 5 | const range = max - min + 1; 6 | const count = new Array(range).fill(0); 7 | const output = new Array(arr.length).fill(0); 8 | 9 | for (let i = 0; i < arr.length; i++) { 10 | count[arr[i] - min]++; 11 | } 12 | 13 | for (let i = 1; i < count.length; i++) { 14 | count[i] += count[i - 1]; 15 | } 16 | 17 | for (let i = arr.length - 1; i >= 0; i--) { 18 | output[count[arr[i] - min] - 1] = arr[i]; 19 | count[arr[i] - min]--; 20 | } 21 | 22 | for (let i = 0; i < arr.length; i++) { 23 | arr[i] = output[i]; 24 | } 25 | 26 | return arr; 27 | }; 28 | export {}; -------------------------------------------------------------------------------- /src/sort/heap.sort.ts: -------------------------------------------------------------------------------- 1 | import { defaultComparator } from "./utilities"; 2 | 3 | Array.prototype.heapSort = function( 4 | compareFn: (a: T, b: T) => number = defaultComparator 5 | ): T[] { 6 | const arr = this as T[]; 7 | const len = arr.length; 8 | 9 | const heapify = (n: number, i: number) => { 10 | let largest = i; 11 | const left = 2 * i + 1; 12 | const right = 2 * i + 2; 13 | 14 | if (left < n && compareFn(arr[left], arr[largest]) > 0) { 15 | largest = left; 16 | } 17 | if (right < n && compareFn(arr[right], arr[largest]) > 0) { 18 | largest = right; 19 | } 20 | 21 | if (largest !== i) { 22 | [arr[i], arr[largest]] = [arr[largest], arr[i]]; 23 | heapify(n, largest); 24 | } 25 | }; 26 | 27 | for (let i = Math.floor(len / 2) - 1; i >= 0; i--) { 28 | heapify(len, i); 29 | } 30 | 31 | for (let i = len - 1; i > 0; i--) { 32 | [arr[0], arr[i]] = [arr[i], arr[0]]; 33 | heapify(i, 0); 34 | } 35 | 36 | return arr; 37 | }; -------------------------------------------------------------------------------- /src/sort/index.ts: -------------------------------------------------------------------------------- 1 | export * from './bubble.sort'; 2 | export * from './selection.sort'; 3 | export * from './radix.sort'; 4 | export * from './insertion.sort'; 5 | export * from './merge.sort'; 6 | export * from './quick.sort'; 7 | export * from './heap.sort'; 8 | export * from './radix.sort'; 9 | export * from './counting.sort'; 10 | export * from './bucket.sort'; 11 | export * from './shell.sort'; 12 | export * from './cocktail.sort'; 13 | export * from './comb.sort'; 14 | export * from './tim.sort'; -------------------------------------------------------------------------------- /src/sort/insertion.sort.ts: -------------------------------------------------------------------------------- 1 | import { defaultComparator } from "./utilities"; 2 | 3 | Array.prototype.insertionSort = function( 4 | compareFn: (a: T, b: T) => number = defaultComparator 5 | ): T[] { 6 | const arr = this as T[]; 7 | const len = arr.length; 8 | 9 | for (let i = 1; i < len; i++) { 10 | let current = arr[i]; 11 | let j = i - 1; 12 | while (j >= 0 && compareFn(arr[j], current) > 0) { 13 | arr[j + 1] = arr[j]; 14 | j--; 15 | } 16 | arr[j + 1] = current; 17 | } 18 | 19 | return arr; 20 | }; -------------------------------------------------------------------------------- /src/sort/merge.sort.ts: -------------------------------------------------------------------------------- 1 | import { defaultComparator } from "./utilities"; 2 | 3 | Array.prototype.mergeSort = function( 4 | compareFn: (a: T, b: T) => number = defaultComparator 5 | ): T[] { 6 | const arr = this as T[]; 7 | 8 | const merge = (left: T[], right: T[]): T[] => { 9 | const result: T[] = []; 10 | let i = 0; 11 | let j = 0; 12 | 13 | while (i < left.length && j < right.length) { 14 | if (compareFn(left[i], right[j]) <= 0) { 15 | result.push(left[i]); 16 | i++; 17 | } else { 18 | result.push(right[j]); 19 | j++; 20 | } 21 | } 22 | 23 | return result.concat(left.slice(i)).concat(right.slice(j)); 24 | }; 25 | 26 | if (arr.length <= 1) { 27 | return arr; 28 | } 29 | 30 | const mid = Math.floor(arr.length / 2); 31 | const left = arr.slice(0, mid).mergeSort(compareFn); 32 | const right = arr.slice(mid).mergeSort(compareFn); 33 | 34 | return merge(left, right); 35 | }; 36 | 37 | export {}; -------------------------------------------------------------------------------- /src/sort/quick.sort.ts: -------------------------------------------------------------------------------- 1 | import { defaultComparator } from "./utilities"; 2 | 3 | Array.prototype.quickSort = function( 4 | compareFn: (a: T, b: T) => number = defaultComparator 5 | ): T[] { 6 | const arr = this as T[]; 7 | 8 | if (arr.length <= 1) { 9 | return arr; 10 | } 11 | 12 | const pivot = arr[Math.floor(arr.length / 2)]; 13 | const left = arr.filter((x) => compareFn(x, pivot) < 0); 14 | const right = arr.filter((x) => compareFn(x, pivot) > 0); 15 | const middle = arr.filter((x) => compareFn(x, pivot) === 0); 16 | 17 | return left.quickSort(compareFn).concat(middle, right.quickSort(compareFn)); 18 | }; -------------------------------------------------------------------------------- /src/sort/radix.sort.ts: -------------------------------------------------------------------------------- 1 | Array.prototype.radixSort = function(): number[] { 2 | const arr = this as number[]; 3 | 4 | const getMax = (arr: number[]): number => Math.max(...arr); 5 | 6 | const countingSort = (arr: number[], exp: number): void => { 7 | const output = new Array(arr.length).fill(0); 8 | const count = new Array(10).fill(0); 9 | 10 | for (let i = 0; i < arr.length; i++) { 11 | const index = Math.floor(arr[i] / exp) % 10; 12 | count[index]++; 13 | } 14 | 15 | for (let i = 1; i < 10; i++) { 16 | count[i] += count[i - 1]; 17 | } 18 | 19 | for (let i = arr.length - 1; i >= 0; i--) { 20 | const index = Math.floor(arr[i] / exp) % 10; 21 | output[count[index] - 1] = arr[i]; 22 | count[index]--; 23 | } 24 | 25 | for (let i = 0; i < arr.length; i++) { 26 | arr[i] = output[i]; 27 | } 28 | }; 29 | 30 | const max = getMax(arr); 31 | for (let exp = 1; Math.floor(max / exp) > 0; exp *= 10) { 32 | countingSort(arr, exp); 33 | } 34 | 35 | return arr; 36 | }; 37 | 38 | export {} -------------------------------------------------------------------------------- /src/sort/selection.sort.ts: -------------------------------------------------------------------------------- 1 | import { defaultComparator } from "./utilities"; 2 | 3 | Array.prototype.selectionSort = function( 4 | compareFn: (a: T, b: T) => number = defaultComparator 5 | ): T[] { 6 | const arr = this as T[]; 7 | const len = arr.length; 8 | 9 | for (let i = 0; i < len; i++) { 10 | let minIndex = i; 11 | for (let j = i + 1; j < len; j++) { 12 | if (compareFn(arr[j], arr[minIndex]) < 0) { 13 | minIndex = j; 14 | } 15 | } 16 | if (minIndex !== i) { 17 | [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]; 18 | } 19 | } 20 | 21 | return arr; 22 | } -------------------------------------------------------------------------------- /src/sort/shell.sort.ts: -------------------------------------------------------------------------------- 1 | import { defaultComparator } from "./utilities"; 2 | 3 | Array.prototype.shellSort = function( 4 | compareFn: (a: T, b: T) => number = defaultComparator 5 | ): T[] { 6 | const arr = this as T[]; 7 | const len = arr.length; 8 | 9 | let gap = Math.floor(len / 2); 10 | while (gap > 0) { 11 | for (let i = gap; i < len; i++) { 12 | const current = arr[i]; 13 | let j = i; 14 | 15 | while (j >= gap && compareFn(arr[j - gap], current) > 0) { 16 | arr[j] = arr[j - gap]; 17 | j -= gap; 18 | } 19 | arr[j] = current; 20 | } 21 | gap = Math.floor(gap / 2); 22 | } 23 | 24 | return arr; 25 | }; 26 | export {} -------------------------------------------------------------------------------- /src/sort/sort.d.ts: -------------------------------------------------------------------------------- 1 | export declare global { 2 | interface Array { 3 | /** 4 | * @default runtime: O(n^2) memory: O(1) 5 | */ 6 | bubbleSort(compareFn?: (a: T, b: T) => number): this; 7 | /** 8 | * @default runtime: O(n^2) memory: O(1) 9 | */ 10 | selectionSort(compareFn?: (a: T, b: T) => number): this; 11 | /** 12 | * @default runtime: O(n^2) memory: O(1) 13 | */ 14 | insertionSort(compareFn?: (a: T, b: T) => number): this; 15 | /** 16 | * @default runtime: O(n log n) memory: O(n) 17 | */ 18 | mergeSort(compareFn?: (a: T, b: T) => number): this; 19 | /** 20 | * @default 21 | * runtime: O(n log n) (average case), O(n^2) (worst case) 22 | * memory: O(log n) (for recursion stack) 23 | */ 24 | quickSort(compareFn?: (a: T, b: T) => number): this; 25 | /** 26 | * @default runtime: O(n log n) memory: O(1) 27 | */ 28 | heapSort(compareFn?: (a: T, b: T) => number): this; 29 | /** 30 | * @default runtime: O(n * k) memory: O(n) 31 | */ 32 | radixSort(): this; 33 | /** 34 | * @default runtime: O(n * k) memory: O(n) 35 | */ 36 | countingSort(): this; 37 | /** 38 | * @default runtime: O(n + k) memory: O(n + k) 39 | */ 40 | bucketSort(): this; 41 | /** 42 | * @default 43 | * runtime: O(n log n) (average case), O(n^2) (worst case) 44 | * memory: O(1) (for recursion stack) 45 | */ 46 | shellSort(compareFn?: (a: T, b: T) => number): this; 47 | /** 48 | * @default runtime: O(n ^ 2) memory: O(1) 49 | */ 50 | cocktailSort(compareFn?: (a: T, b: T) => number): this; 51 | /** 52 | * @default runtime: O(n ^ 2) memory: O(1) 53 | */ 54 | combSort(compareFn?: (a: T, b: T) => number): this; 55 | /** 56 | * @default runtime: O(n log n) memory: O(n) 57 | */ 58 | timSort(compareFn?: (a: T, b: T) => number): this; 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /src/sort/tim.sort.ts: -------------------------------------------------------------------------------- 1 | import { defaultComparator } from "./utilities"; 2 | 3 | // Минимальный размер подмассива, который сортируется с помощью Insertion Sort 4 | const RUN = 32; 5 | 6 | /** 7 | * Выполняет сортировку вставками для подмассива arr[start...end] 8 | */ 9 | function insertionSort( 10 | arr: T[], 11 | start: number, 12 | end: number, 13 | compareFn: (a: T, b: T) => number 14 | ): void { 15 | for (let i = start + 1; i <= end; i++) { 16 | let temp = arr[i]; 17 | let j = i - 1; 18 | while (j >= start && compareFn(arr[j], temp) > 0) { 19 | arr[j + 1] = arr[j]; 20 | j--; 21 | } 22 | arr[j + 1] = temp; 23 | } 24 | } 25 | 26 | /** 27 | * Объединяет два подмассива arr[left...mid] и arr[mid+1...right] 28 | */ 29 | function merge( 30 | arr: T[], 31 | left: number, 32 | mid: number, 33 | right: number, 34 | compareFn: (a: T, b: T) => number 35 | ): void { 36 | const len1 = mid - left + 1; 37 | const len2 = right - mid; 38 | const leftArr = new Array(len1); 39 | const rightArr = new Array(len2); 40 | 41 | for (let i = 0; i < len1; i++) leftArr[i] = arr[left + i]; 42 | for (let i = 0; i < len2; i++) rightArr[i] = arr[mid + 1 + i]; 43 | 44 | let i = 0, j = 0, k = left; 45 | 46 | while (i < len1 && j < len2) { 47 | if (compareFn(leftArr[i], rightArr[j]) <= 0) { 48 | arr[k] = leftArr[i]; 49 | i++; 50 | } else { 51 | arr[k] = rightArr[j]; 52 | j++; 53 | } 54 | k++; 55 | } 56 | 57 | // Копируем оставшиеся элементы, если такие есть 58 | while (i < len1) { 59 | arr[k] = leftArr[i]; 60 | i++; 61 | k++; 62 | } 63 | 64 | while (j < len2) { 65 | arr[k] = rightArr[j]; 66 | j++; 67 | k++; 68 | } 69 | } 70 | 71 | Array.prototype.timSort = function( 72 | compareFn: (a: T, b: T) => number = defaultComparator 73 | ): T[] { 74 | const arr = this as T[]; 75 | const n = arr.length; 76 | 77 | // Сортируем каждый подмассив длиной RUN с помощью Insertion Sort 78 | for (let i = 0; i < n; i += RUN) { 79 | insertionSort(arr, i, Math.min(i + RUN - 1, n - 1), compareFn); 80 | } 81 | 82 | // Сливаем отсортированные подмассивы с помощью Merge 83 | for (let size = RUN; size < n; size = 2 * size) { 84 | for (let left = 0; left < n; left += 2 * size) { 85 | const mid = Math.min(left + size - 1, n - 1); 86 | const right = Math.min(left + 2 * size - 1, n - 1); 87 | 88 | if (mid < right) { 89 | merge(arr, left, mid, right, compareFn); 90 | } 91 | } 92 | } 93 | 94 | return arr; 95 | }; 96 | 97 | export {} -------------------------------------------------------------------------------- /src/sort/utilities.ts: -------------------------------------------------------------------------------- 1 | import { Presets, SingleBar } from "cli-progress"; 2 | 3 | export const defaultComparator = (a: T, b: T) => (a > b ? 1 : a < b ? -1 : 0); 4 | 5 | 6 | 7 | export function testSortingPerformance(sortMethod: keyof Array, arr: T[]): void { 8 | 9 | const uploadingBar = new SingleBar({ 10 | format: `progress |{bar}| {percentage}% | operations {value}/{total}`, 11 | }, Presets.legacy); 12 | 13 | uploadingBar.start(Math.floor(arr.length * Math.log(arr.length)), 0); 14 | 15 | const progressComparator = (a: T, b: T) => { 16 | uploadingBar.increment(); 17 | return (a > b ? 1 : a < b ? -1 : 0) 18 | }; 19 | 20 | // Копируем массив, чтобы сортировки не влияли друг на друга 21 | const arrayToSort = [...arr]; 22 | 23 | // Измеряем время выполнения 24 | console.time(`${String(sortMethod)} time`); 25 | const startMemory = process.memoryUsage().heapUsed; 26 | 27 | // Вызываем метод сортировки, определенный в Array.prototype 28 | (arrayToSort as any)[sortMethod](progressComparator); 29 | 30 | const endMemory = process.memoryUsage().heapUsed; 31 | 32 | uploadingBar.stop(); 33 | console.timeEnd(`${String(sortMethod)} time`); 34 | 35 | 36 | 37 | // Измеряем память 38 | const memoryUsed = (endMemory - startMemory) / 1024 / 1024; 39 | console.log(`${String(sortMethod)} memory usage: ${memoryUsed.toFixed(2)} MB`); 40 | } -------------------------------------------------------------------------------- /src/stack/asteroid-collision.ts: -------------------------------------------------------------------------------- 1 | function asteroidCollision(asteroids: number[]): number[] { 2 | const stack: number[] = []; 3 | for (let i = 0; i < asteroids.length; i++) { 4 | let remain = asteroids[i] 5 | while (stack.length > 0 && remain < 0 && stack[stack.length - 1] > 0) { 6 | const asteroid = stack.pop() 7 | if (asteroid + remain === 0) { 8 | remain = 0; 9 | } else { 10 | remain = asteroid + remain > 0 ? asteroid : remain 11 | } 12 | } 13 | if (remain) { 14 | stack.push(remain) 15 | } 16 | } 17 | return stack; 18 | }; 19 | 20 | export function checkAsteroidCollisionDBG() { 21 | const tests = [ 22 | { 23 | input: [5, 10, -5], 24 | result: [5, 10] 25 | }, 26 | { 27 | input: [8, -8], 28 | result: [] 29 | }, 30 | { 31 | input: [10, 2, -5], 32 | result: [10] 33 | }, 34 | { 35 | input: [-2, -1, 1, 2], 36 | result: [-2, -1, 1, 2] 37 | }, 38 | { 39 | input: [1, -1, -2, 2], 40 | result: [-2, 2] 41 | }, 42 | { 43 | input: [1, 2, 3, -3, -2, -1], 44 | result: [] 45 | } 46 | ]; 47 | 48 | tests.forEach((test, index) => { 49 | const result = asteroidCollision(test.input); 50 | const success = JSON.stringify(result) === JSON.stringify(test.result); 51 | if (success) { 52 | console.log(`${index} success`); 53 | } else { 54 | console.log(`${index} fail`); 55 | console.log(`expected ${JSON.stringify(test.result)}`); 56 | console.log(`got ${JSON.stringify(result)}`); 57 | } 58 | }); 59 | } 60 | -------------------------------------------------------------------------------- /src/stack/decode-string.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @problem [2390. Removing Stars From a String](https://leetcode.com/problems/removing-stars-from-a-string) 3 | */ 4 | function decodeString(s: string): string { 5 | const numStack: number[] = []; 6 | const strStack: string[] = []; 7 | let sb = ""; 8 | let n = 0; 9 | 10 | for (let i = 0; i < s.length; i++) { 11 | const c = s[i]; 12 | 13 | if (/\d/.test(c)) { 14 | n = n * 10 + (c.charCodeAt(0) - '0'.charCodeAt(0)); 15 | } 16 | else if (c === '[') { 17 | numStack.push(n); 18 | strStack.push(sb); 19 | n = 0; 20 | sb = ""; 21 | } 22 | else if (c === ']') { 23 | const k = numStack.pop(); 24 | const prev = strStack.pop(); 25 | sb = prev + sb.repeat(k); 26 | } else { 27 | sb += c; 28 | } 29 | } 30 | return sb; 31 | } 32 | 33 | 34 | export function checkDecodeStringDBG() { 35 | const tests = [ 36 | { 37 | input: "2[abc]3[cd]ef", 38 | result: "abcabccdcdcdef" 39 | }, 40 | { 41 | input: "12[xy]", 42 | result: "xyxyxyxyxyxyxyxyxyxyxyxy" 43 | }, 44 | { 45 | input: "3[a]", 46 | result: "aaa" 47 | }, 48 | { 49 | input: "2[ab]", 50 | result: "abab" 51 | }, 52 | { 53 | input: "1[z]", 54 | result: "z" 55 | }, 56 | { 57 | input: "3[a2[c]]", 58 | result: "accaccacc" 59 | }, 60 | { 61 | input: "2[b3[a]]", 62 | result: "baaabaaa" 63 | }, 64 | { 65 | input: "10[a]", 66 | result: "aaaaaaaaaa" 67 | }, 68 | { 69 | input: "", 70 | result: "" 71 | }, 72 | { 73 | input: "abc", 74 | result: "abc" 75 | }, 76 | { 77 | input: "3[]", 78 | result: "" 79 | }, 80 | { 81 | input: "0[abc]", 82 | result: "" 83 | }, 84 | { 85 | input: "2[a2[b2[c]]]", 86 | result: "abccbccabccbcc" 87 | } 88 | ]; 89 | 90 | tests.forEach((test, index) => { 91 | const result = decodeString(test.input); 92 | const success = result === test.result; 93 | if (success) { 94 | console.log(`${index} success`); 95 | } else { 96 | console.log(`${index} fail`); 97 | console.log(`input: ${JSON.stringify(test.input)}`); 98 | console.log(`expected: ${JSON.stringify(test.result)}`); 99 | console.log(`got: ${JSON.stringify(result)}`); 100 | } 101 | }); 102 | } -------------------------------------------------------------------------------- /src/stack/eval-rpn.ts: -------------------------------------------------------------------------------- 1 | function evalRPN(tokens: string[]): number { 2 | const stack: number[] = []; 3 | 4 | for (let token of tokens) { 5 | if (!isNaN(Number(token))) { 6 | stack.push(Number(token)); 7 | } else { 8 | const b = stack.pop()!; 9 | const a = stack.pop()!; 10 | switch (token) { 11 | case '+': stack.push(a + b); break; 12 | case '-': stack.push(a - b); break; 13 | case '*': stack.push(a * b); break; 14 | case '/': stack.push(a / b); break; 15 | } 16 | } 17 | } 18 | return stack.pop()!; 19 | } 20 | 21 | export function evalRPNDBG(){ 22 | const tests = [ 23 | { 24 | input: ["2", "1", "+", "3", "*"], 25 | result: 9 26 | }, 27 | { 28 | input: ["4", "13", "5", "/", "+"], 29 | result: 6 30 | }, 31 | { 32 | input: ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"], 33 | result: 22 34 | } 35 | ]; 36 | 37 | tests.forEach((test, index) => { 38 | const result = evalRPN(test.input); 39 | if (result === test.result) { 40 | console.log(`${index} success`); 41 | } else { 42 | console.log(`${index} fail`); 43 | console.log(`expected ${test.result}`); 44 | console.log(`got ${result}`); 45 | } 46 | }); 47 | } -------------------------------------------------------------------------------- /src/stack/remove-stars.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @problem [2390. Removing Stars From a String](https://leetcode.com/problems/removing-stars-from-a-string) 3 | */ 4 | function removeStars(s: string): string { 5 | const stack: string[] = []; 6 | for (const char of s) { 7 | if (char !== '*') { 8 | stack.push(char); 9 | } else { 10 | stack.pop(); 11 | } 12 | } 13 | return stack.join(''); 14 | }; -------------------------------------------------------------------------------- /src/stack/simplify-path.ts: -------------------------------------------------------------------------------- 1 | function simplifyPath(path: string): string { 2 | const stack: string[] = []; 3 | const components = path.split('/'); 4 | 5 | for (let component of components) { 6 | if (component === '' || component === '.') { 7 | continue; 8 | } else if (component === '..') { 9 | if (stack.length > 0) { 10 | stack.pop(); 11 | } 12 | } else { 13 | stack.push(component); 14 | } 15 | } 16 | return '/' + stack.join('/'); 17 | } 18 | 19 | export function simplifyPathDBG(){ 20 | const tests = [ 21 | { 22 | input: "/home/", 23 | result: "/home" 24 | }, 25 | { 26 | input: "/../", 27 | result: "/" 28 | }, 29 | { 30 | input: "/home//foo/", 31 | result: "/home/foo" 32 | }, 33 | { 34 | input: "/a/./b/../../c/", 35 | result: "/c" 36 | } 37 | ]; 38 | 39 | tests.forEach((test, index) => { 40 | const result = simplifyPath(test.input); 41 | if (result === test.result) { 42 | console.log(`${index} success`); 43 | } else { 44 | console.log(`${index} fail`); 45 | console.log(`expected ${test.result}`); 46 | console.log(`got ${result}`); 47 | } 48 | }); 49 | } -------------------------------------------------------------------------------- /src/stack/valid-parentheses.ts: -------------------------------------------------------------------------------- 1 | function isValid(s: string): boolean { 2 | const stack: string[] = []; 3 | const map: { [key: string]: string } = { 4 | ')': '(', 5 | ']': '[', 6 | '}': '{', 7 | }; 8 | 9 | for (let char of s) { 10 | if (char === '(' || char === '[' || char === '{') { 11 | stack.push(char); 12 | } else { 13 | if (stack.length === 0 || stack.pop() !== map[char]) { 14 | return false; 15 | } 16 | } 17 | } 18 | 19 | return stack.length === 0; 20 | } 21 | -------------------------------------------------------------------------------- /src/string/binary_search/next-greatest-letter.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @problem [744. Find Smallest Letter Greater Than Target](https://leetcode.com/problems/find-smallest-letter-greater-than-target) 3 | * 4 | * ### Решаем бинарным поиском 5 | * 1. по условию задачи надо понять, что можно сразу отдать первый символ, 6 | * если target больше или равен последнему 7 | * 2. ну а если так нельзя, то bs'ом двигаем left и right до талого и возвращаем 8 | * и возвращаем left (самый маленький) 9 | * 10 | * Ключевой момент в том, чтобы двигать левый указатель, когда символы равны 11 | * ``` 12 | * if (letters[mid] <= target) { 13 | * left = mid + 1; 14 | * } 15 | * ``` 16 | */ 17 | function nextGreatestLetter(letters: string[], target: string): string { 18 | let left = 0, right = letters.length - 1; 19 | 20 | if (target >= letters[right]) { 21 | return letters[0]; 22 | } 23 | while (left <= right) { 24 | const mid = Math.floor((right + left) / 2); 25 | if (letters[mid] <= target) { 26 | left = mid + 1; 27 | } else { 28 | right = mid - 1; 29 | } 30 | } 31 | return letters[left]; 32 | }; -------------------------------------------------------------------------------- /src/string/compress.ts: -------------------------------------------------------------------------------- 1 | function compress(chars: string[]): number { 2 | let write = 0; // Указатель на позицию записи 3 | let read = 0; // Указатель на позицию чтения 4 | 5 | while (read < chars.length) { 6 | const char = chars[read]; // Текущий символ 7 | let count = 0; 8 | 9 | // Считаем количество одинаковых символов подряд 10 | while (read < chars.length && chars[read] === char) { 11 | read++; 12 | count++; 13 | } 14 | 15 | // Записываем текущий символ 16 | chars[write] = char; 17 | write++; 18 | 19 | // Если количество символов больше 1, записываем число 20 | if (count > 1) { 21 | const countStr = count.toString(); 22 | for (let i = 0; i < countStr.length; i++) { 23 | chars[write] = countStr[i]; 24 | write++; 25 | } 26 | } 27 | } 28 | 29 | return write; // Возвращаем новую длину массива 30 | } 31 | 32 | 33 | function compressDBG() { 34 | const tests = [ 35 | { 36 | input: ["a", "a", "b", "b", "c", "c", "c"], 37 | result: ["a", "2", "b", "2", "c", "3"], // Возвращаемое значение: 6 38 | }, 39 | { 40 | input: ["a"], 41 | result: ["a"], // Возвращаемое значение: 1 42 | }, 43 | { 44 | input: ["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b"], 45 | result: ["a", "b", "1", "2"], // Возвращаемое значение: 4 46 | } 47 | ]; 48 | 49 | tests.forEach((test, index) => { 50 | const chars = test.input.slice(); 51 | const newLength = compress(chars); 52 | const success = JSON.stringify(chars.slice(0, newLength)) === JSON.stringify(test.result); 53 | if (success) { 54 | console.log(`${index} success`); 55 | } else { 56 | console.log(`${index} fail`); 57 | console.log(`expected ${JSON.stringify(test.result)}`); 58 | console.log(`got ${JSON.stringify(chars.slice(0, newLength))}`); 59 | } 60 | }); 61 | } -------------------------------------------------------------------------------- /src/string/counting/check-inclusion.ts: -------------------------------------------------------------------------------- 1 | function checkInclusion(s1: string, s2: string): boolean { 2 | if (s1.length > s2.length) return false; 3 | 4 | const count1 = new Array(26).fill(0); 5 | const count2 = new Array(26).fill(0); 6 | const aCharCode = 'a'.charCodeAt(0); 7 | 8 | for (let i = 0; i < s1.length; i++) { 9 | count1[s1.charCodeAt(i) - aCharCode]++; 10 | count2[s2.charCodeAt(i) - aCharCode]++; 11 | } 12 | 13 | if (count1.every((elem, i) => elem === count2[i])){ 14 | return true; 15 | } 16 | 17 | for (let i = s1.length; i < s2.length; i++) { 18 | count2[s2.charCodeAt(i) - aCharCode]++; 19 | count2[s2.charCodeAt(i - s1.length) - aCharCode]--; 20 | 21 | if (count1.every((elem, i) => elem === count2[i])) { 22 | return true; 23 | } 24 | } 25 | 26 | return false; 27 | } 28 | 29 | export function checkInclusionDBG(){ 30 | const tests = [ 31 | { 32 | input: { s1: "ab", s2: "eidbaooo" }, 33 | result: true // Перестановка "ab" есть в "eidbaooo" (индексы 3-4) 34 | }, 35 | { 36 | input: { s1: "ab", s2: "eidboaoo" }, 37 | result: false // Перестановки "ab" нет в "eidboaoo" 38 | } 39 | ]; 40 | 41 | tests.forEach((test, index) => { 42 | const result = checkInclusion(test.input.s1, test.input.s2); 43 | const success = result === test.result; 44 | if (success) { 45 | console.log(`${index} success`); 46 | } else { 47 | console.log(`${index} fail`); 48 | console.log(`expected ${test.result}`); 49 | console.log(`got ${result}`); 50 | } 51 | }); 52 | } -------------------------------------------------------------------------------- /src/string/counting/close-string.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @problem [1657. Determine if Two Strings Are Close](https://leetcode.com/problems/determine-if-two-strings-are-close) 3 | * 4 | * ### Решаем методом подсчёта букв 5 | * Главное понять, что 2 операции из задачи можно делать сколько угодно раз, 6 | * но существуют глобальные ограничения, которые помогут упростить поиск и 7 | * не заниматься перебором всех операций. 8 | * 9 | */ 10 | function closeStrings(word1: string, word2: string): boolean { 11 | if (word1.length !== word2.length) { 12 | return false; 13 | } 14 | const aCharCode = 'a'.charCodeAt(0); 15 | const freq1 = new Array(26).fill(0); 16 | const freq2 = new Array(26).fill(0); 17 | 18 | for (let i = 0; i < word1.length; i++) { 19 | freq1[word1.charCodeAt(i) - aCharCode]++; 20 | freq2[word2.charCodeAt(i) - aCharCode]++; 21 | } 22 | for (let i = 0; i < 26; i++) { 23 | if (freq1[i] === 0 && freq2[i] !== 0) { 24 | return false; 25 | } 26 | if (freq2[i] === 0 && freq1[i] !== 0) { 27 | return false; 28 | } 29 | } 30 | freq1.sort(); 31 | freq2.sort(); 32 | return freq1.every((num, i) => num === freq2[i]); 33 | }; 34 | 35 | 36 | export function checkCloseStringsDBG() { 37 | const tests = [ 38 | { 39 | input: { word1: "abc", word2: "bca" }, 40 | result: true // Содержат одни и те же символы и частоты можно переставить 41 | }, 42 | { 43 | input: { word1: "a", word2: "aa" }, 44 | result: false // У первого одна 'a', у второго — две 45 | }, 46 | { 47 | input: { word1: "cabbba", word2: "abbccc" }, 48 | result: true // Частоты [1,2,3] => [3,2,1], буквы одинаковые 49 | }, 50 | { 51 | input: { word1: "cabbba", word2: "aabbss" }, 52 | result: false // Есть буквы, которых нет в другой строке 53 | }, 54 | { 55 | input: { word1: "uau", word2: "ssx" }, 56 | result: false // Нет совпадающих символов 57 | } 58 | ]; 59 | 60 | tests.forEach((test, index) => { 61 | const result = closeStrings(test.input.word1, test.input.word2); 62 | const success = result === test.result; 63 | if (success) { 64 | console.log(`${index} success`); 65 | } else { 66 | console.log(`${index} fail`); 67 | console.log(`expected ${test.result}`); 68 | console.log(`got ${result}`); 69 | } 70 | }); 71 | } -------------------------------------------------------------------------------- /src/string/find-index-first-occurance.ts: -------------------------------------------------------------------------------- 1 | // using js 2 | function strStr(haystack: string, needle: string): number { 3 | return haystack.indexOf(needle) 4 | }; 5 | 6 | // using algo 7 | function strStrSECOND(haystack: string, needle: string): number { 8 | if (needle.length === 0) return 0; 9 | for (let i = 0; i <= haystack.length - needle.length; i++) { 10 | if (haystack.substring(i, i + needle.length) === needle) { 11 | return i; 12 | } 13 | } 14 | return -1; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/string/find-panagrams.string.ts: -------------------------------------------------------------------------------- 1 | export function findPanagram(alphabet: Set, s: string): string { 2 | const alphabetCount = new Map(); 3 | const requiredCount = alphabet.size; 4 | let formedCount = 0; 5 | let left = 0; 6 | let minLength = Infinity; 7 | let result = ""; 8 | 9 | for (let right = 0; right < s.length; right++) { 10 | const currentChar = s[right]; 11 | 12 | if (alphabet.has(currentChar)) { 13 | alphabetCount.set(currentChar, (alphabetCount.get(currentChar) || 0) + 1); 14 | 15 | if (alphabetCount.get(currentChar) === 1) { 16 | formedCount++; 17 | } 18 | } 19 | 20 | while (formedCount === requiredCount) { 21 | const currentLength = right - left + 1; 22 | if (currentLength < minLength) { 23 | minLength = currentLength; 24 | result = s.substring(left, right + 1); 25 | } 26 | const leftChar = s[left]; 27 | if (alphabet.has(leftChar)) { 28 | alphabetCount.set(leftChar, alphabetCount.get(leftChar)! - 1); 29 | if (alphabetCount.get(leftChar) === 0) { 30 | formedCount--; 31 | } 32 | } 33 | left++; 34 | } 35 | } 36 | 37 | return result; 38 | } -------------------------------------------------------------------------------- /src/string/group-anagrams.ts: -------------------------------------------------------------------------------- 1 | function groupAnagrams(strs: string[]): string[][] { 2 | const map = new Map(); 3 | const charCodeA = 'a'.charCodeAt(0); 4 | strs.forEach(str => { 5 | const count = new Array(26).fill(0); 6 | for (let char of str) { 7 | count[char.charCodeAt(0) - charCodeA]++; 8 | } 9 | const key = count.join('#'); 10 | if (map.has(key)) { 11 | map.get(key)?.push(str); 12 | } else { 13 | map.set(key, [str]); 14 | } 15 | }); 16 | 17 | return Array.from(map.values()); 18 | } 19 | -------------------------------------------------------------------------------- /src/string/hash_table/is-isomorfic.ts: -------------------------------------------------------------------------------- 1 | function isIsomorphic(s: string, t: string): boolean { 2 | if (s.length !== t.length) return false; 3 | 4 | const mapSToT = new Map(); 5 | const mapTToS = new Map(); 6 | 7 | for (let i = 0; i < s.length; i++) { 8 | const charS = s[i]; 9 | const charT = t[i]; 10 | 11 | if (!mapSToT.has(charS)) { 12 | mapSToT.set(charS, charT); 13 | } else if (mapSToT.get(charS) !== charT) { 14 | return false; 15 | } 16 | 17 | if (!mapTToS.has(charT)) { 18 | mapTToS.set(charT, charS); 19 | } else if (mapTToS.get(charT) !== charS) { 20 | return false; 21 | } 22 | } 23 | 24 | return true; 25 | } 26 | 27 | function isIsomorphicDBG(){ 28 | const tests = [ 29 | { 30 | input: { s: "egg", t: "add" }, 31 | result: true 32 | }, 33 | { 34 | input: { s: "foo", t: "bar" }, 35 | result: false 36 | }, 37 | { 38 | input: { s: "paper", t: "title" }, 39 | result: true 40 | } 41 | ]; 42 | 43 | tests.forEach((test, index) => { 44 | const result = isIsomorphic(test.input.s, test.input.t); 45 | if (result === test.result) { 46 | console.log(`${index} success`); 47 | } else { 48 | console.log(`${index} fail`); 49 | console.log(`expected ${test.result}`); 50 | console.log(`got ${result}`); 51 | } 52 | }); 53 | } -------------------------------------------------------------------------------- /src/string/is-one-edit-distance.ts: -------------------------------------------------------------------------------- 1 | function isOneEditDistance(s: string, t: string): boolean { 2 | const lenS = s.length; 3 | const lenT = t.length; 4 | 5 | if (Math.abs(lenS - lenT) > 1) { 6 | return false; 7 | } 8 | if (lenS > lenT) { 9 | return isOneEditDistance(t, s); 10 | } 11 | for (let i = 0; i < lenS; i++) { 12 | if (s[i] !== t[i]) { 13 | if (lenS === lenT) { 14 | return s.slice(i + 1) === t.slice(i + 1); 15 | } else { 16 | return s.slice(i) === t.slice(i + 1); 17 | } 18 | } 19 | } 20 | return true; 21 | } 22 | 23 | export const isOneEditDistanceDBG = () => { 24 | const tests = [ 25 | { 26 | input: { s: "120031", t: "120032" }, 27 | result: true 28 | }, 29 | { 30 | input: { s: "ab", t: "acb" }, 31 | result: true 32 | }, 33 | { 34 | input: { s: "cab", t: "ad" }, 35 | result: false 36 | }, 37 | { 38 | input: { s: "1203", t: "1213" }, 39 | result: true 40 | }, 41 | 42 | ]; 43 | 44 | tests.forEach((test, index) => { 45 | const result = isOneEditDistance(test.input.s, test.input.t); 46 | if (result === test.result) { 47 | console.log(`${index} success`); 48 | } else { 49 | console.log(`${index} fail`); 50 | console.log(`expected ${test.result}`); 51 | console.log(`got ${result}`); 52 | } 53 | }); 54 | } -------------------------------------------------------------------------------- /src/string/is-palindrome.ts: -------------------------------------------------------------------------------- 1 | function isPalindrome(s: string): boolean { 2 | const cleanedString = s.toLowerCase().replaceAll(/[^a-z0-9]/g, ''); 3 | return cleanedString === cleanedString.split('').reverse().join(''); 4 | } 5 | 6 | export function isPalindromeDBG() { 7 | const tests = [ 8 | { 9 | input: "A man, a plan, a canal: Panama", 10 | result: true 11 | }, 12 | { 13 | input: "race a car", 14 | result: false 15 | }, 16 | { 17 | input: " ", 18 | result: true 19 | } 20 | ]; 21 | 22 | tests.forEach((test, index) => { 23 | const result = isPalindrome(test.input); 24 | if (result === test.result) { 25 | console.log(`${index} success`); 26 | } else { 27 | console.log(`${index} fail`); 28 | console.log(`expected ${test.result}`); 29 | console.log(`got ${result}`); 30 | } 31 | }); 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/string/partition-labels.ts: -------------------------------------------------------------------------------- 1 | function partitionLabels(s: string): number[] { 2 | const last: Record = {}; 3 | const result: number[] = []; 4 | 5 | for (let i = 0; i < s.length; i++) { 6 | last[s[i]] = i; 7 | } 8 | 9 | let start = 0; 10 | let end = 0; 11 | 12 | for (let i = 0; i < s.length; i++) { 13 | end = Math.max(end, last[s[i]]); 14 | if (i === end) { 15 | result.push(end - start + 1); 16 | start = i + 1; 17 | } 18 | } 19 | 20 | return result; 21 | }; 22 | 23 | export function partitionLabelsDBG(){ 24 | const tests = [ 25 | { 26 | input: "ababcbacadefegdehijhklij", 27 | expected: [9, 7, 8] 28 | }, 29 | { 30 | input: "eccbbbbdec", 31 | expected: [10] 32 | } 33 | ]; 34 | 35 | tests.forEach((testCase, index) => { 36 | const result = partitionLabels(testCase.input); 37 | const success = JSON.stringify(result) === JSON.stringify(testCase.expected); 38 | if (success) { 39 | console.log(`Test ${index} success`); 40 | } else { 41 | console.log(`Test ${index} fail`); 42 | console.log(`expected: ${testCase.expected}`); 43 | console.log(`got: ${result}`); 44 | } 45 | }); 46 | } -------------------------------------------------------------------------------- /src/string/remove-invalid-parentheses.ts: -------------------------------------------------------------------------------- 1 | function removeInvalidParentheses(s: string): string[] { 2 | 3 | const isValid = (str: string): boolean => { 4 | let count = 0; 5 | for (let char of str) { 6 | if (char === '(') count++; 7 | if (char === ')') count--; 8 | if (count < 0) return false; 9 | } 10 | return count === 0; 11 | }; 12 | 13 | const result: string[] = []; 14 | const queue = [s]; 15 | const visited = new Set(); 16 | let found = false; 17 | 18 | while (queue.length > 0) { 19 | const current = queue.shift()!; 20 | if (isValid(current)) { 21 | result.push(current); 22 | found = true; 23 | } 24 | if (found) continue; 25 | 26 | for (let i = 0; i < current.length; i++) { 27 | if (current[i] !== '(' && current[i] !== ')') continue; 28 | const next = current.slice(0, i) + current.slice(i + 1); 29 | if (!visited.has(next)) { 30 | queue.push(next); 31 | visited.add(next); 32 | } 33 | } 34 | } 35 | 36 | return result.length ? result : [""]; 37 | } 38 | 39 | 40 | 41 | function removeInvalidParenthesesDBG(){ 42 | const tests = [ 43 | { 44 | input: "()())()", 45 | result: ["()()()", "(())()"] 46 | }, 47 | { 48 | input: "(a)())()", 49 | result: ["(a)()()", "(a())()"] 50 | }, 51 | { 52 | input: ")(", 53 | result: [""] 54 | } 55 | ]; 56 | 57 | tests.forEach((test, index) => { 58 | const result = removeInvalidParentheses(test.input); 59 | const success = JSON.stringify(result.sort()) === JSON.stringify(test.result.sort()); 60 | if (success) { 61 | console.log(`${index} success`); 62 | } else { 63 | console.log(`${index} fail`); 64 | console.log(`expected ${JSON.stringify(test.result)}`); 65 | console.log(`got ${JSON.stringify(result)}`); 66 | } 67 | }); 68 | } 69 | -------------------------------------------------------------------------------- /src/string/robot-return-to-origin.ts: -------------------------------------------------------------------------------- 1 | function judgeCircle(moves: string): boolean { 2 | let x = 0, y = 0; 3 | 4 | for (let move of moves) { 5 | if (move === 'U') { 6 | y++; 7 | } else if (move === 'D') { 8 | y--; 9 | } else if (move === 'R') { 10 | x++; 11 | } else if (move === 'L') { 12 | x--; 13 | } 14 | } 15 | 16 | // Робот вернется в исходную точку, если x и y равны 0 17 | return x === 0 && y === 0; 18 | } 19 | -------------------------------------------------------------------------------- /src/string/sliding_window/find-anagrams.ts: -------------------------------------------------------------------------------- 1 | function findAnagrams(s: string, p: string): number[] { 2 | if (s.length < p.length) return []; 3 | 4 | const result: number[] = []; 5 | const pCount = new Array(26).fill(0); 6 | const sCount = new Array(26).fill(0); 7 | 8 | const aCharCode = 'a'.charCodeAt(0); 9 | 10 | for (let i = 0; i < p.length; i++) { 11 | pCount[p.charCodeAt(i) - aCharCode]++; 12 | sCount[s.charCodeAt(i) - aCharCode]++; 13 | } 14 | if (pCount.every((elem, i) => elem === sCount[i])) { 15 | result.push(0); 16 | } 17 | for (let i = p.length; i < s.length; i++) { 18 | sCount[s.charCodeAt(i) - aCharCode]++; 19 | sCount[s.charCodeAt(i - p.length) - aCharCode]--; 20 | 21 | if (pCount.every((elem, i) => elem === sCount[i])) { 22 | result.push(i - p.length + 1); 23 | } 24 | } 25 | 26 | return result; 27 | } 28 | 29 | export function findAnagramsDBG() { 30 | const tests = [ 31 | { 32 | input: { s: "cbaebabacd", p: "abc" }, 33 | result: [0, 6] // Анаграммы "abc" начинаются с индексов 0 и 6 34 | }, 35 | { 36 | input: { s: "abab", p: "ab" }, 37 | result: [0, 1, 2] // Анаграммы "ab" начинаются с индексов 0, 1, и 2 38 | } 39 | ]; 40 | 41 | tests.forEach((test, index) => { 42 | const result = findAnagrams(test.input.s, test.input.p); 43 | const success = JSON.stringify(result) === JSON.stringify(test.result); 44 | if (success) { 45 | console.log(`${index} success`); 46 | } else { 47 | console.log(`${index} fail`); 48 | console.log(`expected ${test.result}`); 49 | console.log(`got ${result}`); 50 | } 51 | }); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/string/sliding_window/longest-repeating-character-replacement.ts: -------------------------------------------------------------------------------- 1 | function characterReplacement(s: string, k: number): number { 2 | const count = new Array(26).fill(0); 3 | const charCodeA = 'A'.charCodeAt(0); 4 | let maxCount = 0; 5 | let maxLength = 0; 6 | let left = 0; 7 | 8 | for (let right = 0; right < s.length; right++) { 9 | const index = s.charCodeAt(right) - charCodeA; 10 | count[index]++; 11 | maxCount = Math.max(maxCount, count[index]); 12 | 13 | if (right - left + 1 - maxCount > k) { 14 | const leftIndex = s.charCodeAt(left) - charCodeA; 15 | count[leftIndex]--; 16 | left++; 17 | } 18 | maxLength = Math.max(maxLength, right - left + 1); 19 | } 20 | 21 | return maxLength; 22 | }; 23 | 24 | export function characterReplacementDBG() { 25 | const tests = [ 26 | { 27 | input: { s: "ABAB", k: 2 }, 28 | expected: 4 // Можно заменить 2 символа 'A' на 'B' или наоборот, длина подстроки 4 29 | }, 30 | { 31 | input: { s: "AABABBA", k: 1 }, 32 | expected: 4 // После одной замены можно получить подстроку длиной 4 (например, "AABA" или "BBBA") 33 | } 34 | ]; 35 | 36 | tests.forEach((testCase, index) => { 37 | const result = characterReplacement(testCase.input.s, testCase.input.k); 38 | const success = result === testCase.expected; 39 | if (success) { 40 | console.log(`Test ${index} success`); 41 | } else { 42 | console.log(`Test ${index} fail`); 43 | console.log(`expected: ${testCase.expected}`); 44 | console.log(`got: ${result}`); 45 | } 46 | }); 47 | } -------------------------------------------------------------------------------- /src/string/substring/longest-substring-k-distinct.ts: -------------------------------------------------------------------------------- 1 | function lengthOfLongestSubstringKDistinct(s: string, k: number): number { 2 | if (k === 0 || s.length === 0) return 0; 3 | 4 | const map: { [key: string]: number } = {}; 5 | let left = 0; 6 | let maxLength = 0; 7 | 8 | for (let right = 0; right < s.length; right++) { 9 | const char = s[right]; 10 | map[char] = (map[char] || 0) + 1; 11 | 12 | while (Object.keys(map).length > k) { 13 | const leftChar = s[left]; 14 | map[leftChar]--; 15 | if (map[leftChar] === 0) { 16 | delete map[leftChar]; 17 | } 18 | left++; 19 | } 20 | 21 | maxLength = Math.max(maxLength, right - left + 1); 22 | } 23 | 24 | return maxLength; 25 | } 26 | 27 | 28 | export function lengthOfLongestSubstringKDistinctDBG(){ 29 | const tests = [ 30 | { 31 | input: { s: "eceba", k: 2 }, 32 | result: 3 // "ece" 33 | }, 34 | { 35 | input: { s: "aa", k: 1 }, 36 | result: 2 // "aa" 37 | } 38 | ]; 39 | 40 | tests.forEach((test, index) => { 41 | const result = lengthOfLongestSubstringKDistinct(test.input.s, test.input.k); 42 | const success = result === test.result; 43 | if (success) { 44 | console.log(`${index} success`); 45 | } else { 46 | console.log(`${index} fail`); 47 | console.log(`expected ${test.result}`); 48 | console.log(`got ${result}`); 49 | } 50 | }); 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/string/substring/longest-substring.ts: -------------------------------------------------------------------------------- 1 | export function lengthOfLongestSubstring(s: string): number { 2 | let maxLength = 0; 3 | let left = 0; 4 | const charIndexMap = new Map(); 5 | 6 | for (let right = 0; right < s.length; right++) { 7 | const char = s[right]; 8 | 9 | if (charIndexMap.has(char) && charIndexMap.get(char) >= left) { 10 | left = charIndexMap.get(char)! + 1; 11 | } 12 | charIndexMap.set(char, right); 13 | maxLength = Math.max(maxLength, right - left + 1); 14 | } 15 | return maxLength; 16 | } 17 | 18 | 19 | export const lengthOfLongestSubstringDBG = () => { 20 | const tests = [ 21 | { 22 | input: "abcabcdbb", 23 | result: 4, // "abc" 24 | }, 25 | { 26 | input: "bbbbb", 27 | result: 1, // "b" 28 | }, 29 | { 30 | input: "pwwkew", 31 | result: 3, // "wke" 32 | }, 33 | { 34 | input: "", 35 | result: 0, // пустая строка 36 | } 37 | ]; 38 | 39 | tests.forEach((test, index) => { 40 | const result = lengthOfLongestSubstring(test.input); 41 | if (result === test.result) { 42 | console.log(`${index} success`); 43 | } else { 44 | console.log(`${index} fail`); 45 | console.log(`expected ${test.result}`); 46 | console.log(`got ${result}`); 47 | } 48 | }); 49 | } -------------------------------------------------------------------------------- /src/string/substring/min-window-substring.ts: -------------------------------------------------------------------------------- 1 | function minWindow(s: string, t: string): string { 2 | if (s.length === 0 || t.length === 0) return ""; 3 | 4 | const tFreq: { [char: string]: number } = {}; 5 | for (let char of t) { 6 | tFreq[char] = (tFreq[char] || 0) + 1; 7 | } 8 | 9 | let required = Object.keys(tFreq).length; 10 | let left = 0, right = 0; 11 | let formed = 0; 12 | const windowCounts: { [char: string]: number } = {}; 13 | 14 | let minLength = Infinity; 15 | let minLeft = 0; 16 | 17 | while (right < s.length) { 18 | const char = s[right]; 19 | windowCounts[char] = (windowCounts[char] || 0) + 1; 20 | 21 | if (tFreq[char] && windowCounts[char] === tFreq[char]) { 22 | formed++; 23 | } 24 | while (left <= right && formed === required) { 25 | if (right - left + 1 < minLength) { 26 | minLength = right - left + 1; 27 | minLeft = left; 28 | } 29 | const leftChar = s[left]; 30 | windowCounts[leftChar]--; 31 | if (tFreq[leftChar] && windowCounts[leftChar] < tFreq[leftChar]) { 32 | formed--; 33 | } 34 | left++; 35 | } 36 | right++; 37 | } 38 | return minLength === Infinity ? "" : s.substring(minLeft, minLeft + minLength); 39 | } 40 | 41 | export function minWindowDBG(){ 42 | const tests = [ 43 | { 44 | input: { s: "ADOBECODEBANC", t: "ABC" }, 45 | result: "BANC" 46 | }, 47 | { 48 | input: { s: "a", t: "a" }, 49 | result: "a" 50 | }, 51 | { 52 | input: { s: "a", t: "aa" }, 53 | result: "" 54 | } 55 | ]; 56 | 57 | tests.forEach((test, index) => { 58 | const result = minWindow(test.input.s, test.input.t); 59 | if (result === test.result) { 60 | console.log(`${index} success`); 61 | } else { 62 | console.log(`${index} fail`); 63 | console.log(`expected ${test.result}`); 64 | console.log(`got ${result}`); 65 | } 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /src/string/substring/repeated-substring-pattern.ts: -------------------------------------------------------------------------------- 1 | namespace RepeatedSubstringPattern { 2 | export function brutForce(s: string): boolean { 3 | const n = s.length; 4 | 5 | for (let len = 1; len <= Math.floor(n / 2); len++) { 6 | if (n % len === 0) { 7 | const substring = s.slice(0, len); 8 | const repeated = substring.repeat(n / len); 9 | if (repeated === s) { 10 | return true; 11 | } 12 | } 13 | } 14 | 15 | return false; 16 | } 17 | 18 | export function multiplyTwice(s: string): boolean { 19 | return (s + s).slice(1, -1).includes(s); 20 | } 21 | } 22 | 23 | export function repeatedSubstringPatternDBG() { 24 | const tests = [ 25 | { 26 | input: "abab", 27 | expected: true // Строка "abab" состоит из повторяющейся подстроки "ab" 28 | }, 29 | { 30 | input: "aba", 31 | expected: false // Строка "aba" не может быть построена из повторяющейся подстроки 32 | }, 33 | { 34 | input: "abcabcabcabc", 35 | expected: true // Строка "abcabcabcabc" состоит из повторяющейся подстроки "abc" 36 | } 37 | ]; 38 | 39 | tests.forEach((testCase, index) => { 40 | const result = RepeatedSubstringPattern.multiplyTwice(testCase.input); 41 | const success = result === testCase.expected; 42 | if (success) { 43 | console.log(`Test ${index} success`); 44 | } else { 45 | console.log(`Test ${index} fail`); 46 | console.log(`expected: ${testCase.expected}`); 47 | console.log(`got: ${result}`); 48 | } 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /src/string/two_pointers/check-inclusion.ts: -------------------------------------------------------------------------------- 1 | function checkInclusion(s1: string, s2: string): boolean { 2 | if (s1.length > s2.length) return false; 3 | 4 | const count1 = new Array(26).fill(0); 5 | const count2 = new Array(26).fill(0); 6 | const aCharCode = 'a'.charCodeAt(0); 7 | 8 | for (let i = 0; i < s1.length; i++) { 9 | count1[s1.charCodeAt(i) - aCharCode]++; 10 | count2[s2.charCodeAt(i) - aCharCode]++; 11 | } 12 | 13 | if (count1.every((elem, i) => elem === count2[i])){ 14 | return true; 15 | } 16 | 17 | for (let i = s1.length; i < s2.length; i++) { 18 | count2[s2.charCodeAt(i) - aCharCode]++; 19 | count2[s2.charCodeAt(i - s1.length) - aCharCode]--; 20 | 21 | if (count1.every((elem, i) => elem === count2[i])) { 22 | return true; 23 | } 24 | } 25 | 26 | return false; 27 | } 28 | 29 | export function checkInclusionDBG(){ 30 | const tests = [ 31 | { 32 | input: { s1: "ab", s2: "eidbaooo" }, 33 | result: true // Перестановка "ab" есть в "eidbaooo" (индексы 3-4) 34 | }, 35 | { 36 | input: { s1: "ab", s2: "eidboaoo" }, 37 | result: false // Перестановки "ab" нет в "eidboaoo" 38 | } 39 | ]; 40 | 41 | tests.forEach((test, index) => { 42 | const result = checkInclusion(test.input.s1, test.input.s2); 43 | const success = result === test.result; 44 | if (success) { 45 | console.log(`${index} success`); 46 | } else { 47 | console.log(`${index} fail`); 48 | console.log(`expected ${test.result}`); 49 | console.log(`got ${result}`); 50 | } 51 | }); 52 | } -------------------------------------------------------------------------------- /src/string/two_pointers/is-subsequence.ts: -------------------------------------------------------------------------------- 1 | function isSubsequence(s: string, t: string): boolean { 2 | let i = 0; 3 | let j = 0; 4 | 5 | while (i < s.length && j < t.length) { 6 | if (s[i] === t[j]) { 7 | i++; 8 | } 9 | j++; 10 | } 11 | 12 | return i === s.length; 13 | } 14 | -------------------------------------------------------------------------------- /src/string/two_pointers/longest-palindrome.ts: -------------------------------------------------------------------------------- 1 | function longestPalindrome(s: string): string { 2 | let longest = ""; 3 | if (s.length < 1 || s === null) { 4 | return longest; 5 | } 6 | const expandFromMiddle = (s: string, l: number, r: number) => { 7 | while (l >= 0 && r < s.length && s[l] === s[r]) { 8 | l--; 9 | r++; 10 | } 11 | return s.substring(l + 1, r); 12 | } 13 | for (let i = 0; i < s.length; i++) { 14 | const longestOdd = expandFromMiddle(s, i, i); 15 | const longestEven = expandFromMiddle(s, i, i + 1); 16 | 17 | if (longestOdd.length > longest.length) { 18 | longest = longestOdd; 19 | } 20 | if (longestEven.length > longest.length) { 21 | longest = longestEven; 22 | } 23 | } 24 | return longest; 25 | }; 26 | 27 | function longestPalindromeSECOND(s: string): string { 28 | if (s.length < 2) return s; 29 | 30 | let start = 0; 31 | let maxLength = 0; 32 | 33 | const expandAroundCenter = (l: number, r: number) => { 34 | while (l >= 0 && r < s.length && s[l] === s[r]) { 35 | if (r - l > maxLength) { 36 | start = l; 37 | maxLength = r - l; 38 | } 39 | l--; 40 | r++; 41 | } 42 | }; 43 | 44 | for (let i = 0; i < s.length; i++) { 45 | expandAroundCenter(i, i); 46 | expandAroundCenter(i, i + 1); 47 | } 48 | 49 | return s.slice(start, start + maxLength + 1); 50 | } 51 | 52 | export function longestPalindromeDBG(){ 53 | 54 | const tests = [ 55 | { 56 | input: "babad", 57 | result: "bab" // или "aba", оба ответа верны 58 | }, 59 | { 60 | input: "cbbd", 61 | result: "bb" 62 | }, 63 | { 64 | input: "a", 65 | result: "a" 66 | }, 67 | { 68 | input: "ac", 69 | result: "a" // или "c", оба ответа верны 70 | } 71 | ]; 72 | 73 | tests.forEach((test, index) => { 74 | const result = longestPalindromeSECOND(test.input); 75 | if (result === test.result) { 76 | console.log(`${index} success`); 77 | } else { 78 | console.log(`${index} fail`); 79 | console.log(`expected ${test.result}`); 80 | console.log(`got ${result}`); 81 | } 82 | }); 83 | } -------------------------------------------------------------------------------- /src/string/two_pointers/valid-palindrome.ts: -------------------------------------------------------------------------------- 1 | function validPalindrome(s: string): boolean { 2 | 3 | const isPalindrome = (left: number, right: number): boolean => { 4 | while (left < right) { 5 | if (s[left] !== s[right]) { 6 | return false; 7 | } 8 | left++; 9 | right--; 10 | } 11 | return true; 12 | }; 13 | 14 | let left = 0; 15 | let right = s.length - 1; 16 | 17 | while (left < right) { 18 | if (s[left] !== s[right]) { 19 | // Пробуем удалить один символ с одной из сторон 20 | return isPalindrome(left + 1, right) || isPalindrome(left, right - 1); 21 | } 22 | left++; 23 | right--; 24 | } 25 | 26 | return true; 27 | } 28 | 29 | export function testValidPalindrome(){ 30 | const tests = [ 31 | { 32 | input: "abca", 33 | result: true // Можно удалить символ 'b' или 'c', чтобы строка стала палиндромом ("aca" или "aba") 34 | }, 35 | { 36 | input: "racecar", 37 | result: true // Строка уже является палиндромом 38 | }, 39 | { 40 | input: "abc", 41 | result: false // Невозможно сделать палиндромом, удалив ровно один символ 42 | } 43 | ]; 44 | 45 | tests.forEach((test, index) => { 46 | const result = validPalindrome(test.input); 47 | const success = result === test.result; 48 | if (success) { 49 | console.log(`${index} success`); 50 | } else { 51 | console.log(`${index} fail`); 52 | console.log(`expected ${test.result}`); 53 | console.log(`got ${result}`); 54 | } 55 | }); 56 | } -------------------------------------------------------------------------------- /src/templates/problem.template.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @problem [1. Problem Name](https://leetcode.com/...) 3 | * 4 | * ### Как решается 5 | * ... 6 | */ 7 | function problem(args: any): number { 8 | return 0; 9 | } -------------------------------------------------------------------------------- /src/typescript/dot-path.ts: -------------------------------------------------------------------------------- 1 | interface Change { 2 | name: string; 3 | upp: number; 4 | fee: number; 5 | aum: number; 6 | capacity: number; 7 | cooldown: number; 8 | expiredAt: string; 9 | createdAt: string; 10 | } 11 | 12 | interface Item { 13 | id: number; 14 | changes: Change; 15 | } 16 | 17 | interface PaginatedData { 18 | items: Item[]; 19 | totalCount: number; 20 | hasNextPage: boolean; 21 | hasPreviousPage: boolean; 22 | } 23 | 24 | type DotPaths = 25 | T extends Array 26 | ? DotPaths 27 | : T extends object 28 | ? { 29 | [K in keyof T & string]: 30 | | `${Prefix}${K}` 31 | | DotPaths; 32 | }[keyof T & string] 33 | : never; 34 | 35 | type PaginatedDataPaths = DotPaths; 36 | -------------------------------------------------------------------------------- /test/test.ts: -------------------------------------------------------------------------------- 1 | console.log() -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@sorts": ["src/sort/index"] 5 | } 6 | }, 7 | "extends": "./tsconfig.json", 8 | "exclude": ["node_modules", "dist" ] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "ES2023", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": false, 16 | "noImplicitAny": false, 17 | "strictBindCallApply": false, 18 | "forceConsistentCasingInFileNames": false, 19 | "noFallthroughCasesInSwitch": false, 20 | "esModuleInterop": true, 21 | }, 22 | "include": ["src", "test"] 23 | } 24 | --------------------------------------------------------------------------------